summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2011-12-03 13:29:22 +0100
committerPierre Schmitz <pierre@archlinux.de>2011-12-03 13:29:22 +0100
commitca32f08966f1b51fcb19460f0996bb0c4048e6fe (patch)
treeec04cc15b867bc21eedca904cea9af0254531a11 /tests
parenta22fbfc60f36f5f7ee10d5ae6fe347340c2ee67c (diff)
Update to MediaWiki 1.18.0
* also update ArchLinux skin to chagnes in MonoBook * Use only css to hide our menu bar when printing
Diffstat (limited to 'tests')
-rw-r--r--tests/.htaccess1
-rw-r--r--tests/RunSeleniumTests.php233
-rw-r--r--tests/TestsAutoLoader.php33
-rw-r--r--tests/parser/extraParserTests.txtbin0 -> 1261 bytes
-rw-r--r--tests/parser/parserTest.inc1280
-rw-r--r--tests/parser/parserTests.txt8824
-rw-r--r--tests/parser/parserTestsParserHook.php46
-rw-r--r--tests/parser/parserTestsStaticParserHook.php58
-rw-r--r--tests/parser/preprocess/All_system_messages.expected5646
-rw-r--r--tests/parser/preprocess/All_system_messages.txt5645
-rw-r--r--tests/parser/preprocess/Factorial.expected17
-rw-r--r--tests/parser/preprocess/Factorial.txt16
-rw-r--r--tests/parser/preprocess/Fundraising.expected18
-rw-r--r--tests/parser/preprocess/Fundraising.txt17
-rw-r--r--tests/parser/preprocess/QuoteQuran.expected140
-rw-r--r--tests/parser/preprocess/QuoteQuran.txt139
-rw-r--r--tests/parserTests.php91
-rw-r--r--tests/phpunit/Makefile82
-rw-r--r--tests/phpunit/MediaWikiLangTestCase.php33
-rw-r--r--tests/phpunit/MediaWikiPHPUnitCommand.php46
-rw-r--r--tests/phpunit/MediaWikiTestCase.php239
-rw-r--r--tests/phpunit/README53
-rw-r--r--tests/phpunit/TODO10
-rw-r--r--tests/phpunit/bootstrap.php32
-rw-r--r--tests/phpunit/data/media/1bit-png.pngbin0 -> 167 bytes
-rw-r--r--tests/phpunit/data/media/Animated_PNG_example_bouncing_beach_ball.pngbin0 -> 72209 bytes
-rw-r--r--tests/phpunit/data/media/Gtk-media-play-ltr.svg35
-rw-r--r--tests/phpunit/data/media/Png-native-test.pngbin0 -> 4665 bytes
-rw-r--r--tests/phpunit/data/media/QA_icon.svg77
-rw-r--r--tests/phpunit/data/media/README38
-rw-r--r--tests/phpunit/data/media/US_states_by_total_state_tax_revenue.svg248
-rw-r--r--tests/phpunit/data/media/Wikimedia-logo.svg14
-rw-r--r--tests/phpunit/data/media/Xmp-exif-multilingual_test.jpgbin0 -> 12544 bytes
-rw-r--r--tests/phpunit/data/media/animated-xmp.gifbin0 -> 3864 bytes
-rw-r--r--tests/phpunit/data/media/animated.gifbin0 -> 497 bytes
-rw-r--r--tests/phpunit/data/media/broken_exif_date.jpgbin0 -> 3233 bytes
-rw-r--r--tests/phpunit/data/media/exif-gps.jpgbin0 -> 665 bytes
-rw-r--r--tests/phpunit/data/media/exif-user-comment.jpgbin0 -> 484 bytes
-rw-r--r--tests/phpunit/data/media/greyscale-na-png.pngbin0 -> 365 bytes
-rw-r--r--tests/phpunit/data/media/greyscale-png.pngbin0 -> 415 bytes
-rw-r--r--tests/phpunit/data/media/iptc-timetest-invalid.jpgbin0 -> 9573 bytes
-rw-r--r--tests/phpunit/data/media/iptc-timetest.jpgbin0 -> 9573 bytes
-rw-r--r--tests/phpunit/data/media/jpeg-comment-binary.jpgbin0 -> 448 bytes
-rw-r--r--tests/phpunit/data/media/jpeg-comment-iso8859-1.jpgbin0 -> 447 bytes
-rw-r--r--tests/phpunit/data/media/jpeg-comment-multiple.jpgbin0 -> 431 bytes
-rw-r--r--tests/phpunit/data/media/jpeg-comment-utf.jpgbin0 -> 445 bytes
-rw-r--r--tests/phpunit/data/media/jpeg-iptc-bad-hash.jpgbin0 -> 499 bytes
-rw-r--r--tests/phpunit/data/media/jpeg-iptc-good-hash.jpgbin0 -> 499 bytes
-rw-r--r--tests/phpunit/data/media/jpeg-padding-even.jpgbin0 -> 450 bytes
-rw-r--r--tests/phpunit/data/media/jpeg-padding-odd.jpgbin0 -> 451 bytes
-rw-r--r--tests/phpunit/data/media/jpeg-xmp-alt.jpgbin0 -> 3255 bytes
-rw-r--r--tests/phpunit/data/media/jpeg-xmp-psir.jpgbin0 -> 3308 bytes
-rw-r--r--tests/phpunit/data/media/jpeg-xmp-psir.xmp35
-rw-r--r--tests/phpunit/data/media/landscape-plain.jpgbin0 -> 38771 bytes
-rw-r--r--tests/phpunit/data/media/nonanimated.gifbin0 -> 200 bytes
-rw-r--r--tests/phpunit/data/media/portrait-rotated.jpgbin0 -> 38577 bytes
-rw-r--r--tests/phpunit/data/media/rgb-na-png.pngbin0 -> 593 bytes
-rw-r--r--tests/phpunit/data/media/rgb-png.pngbin0 -> 663 bytes
-rw-r--r--tests/phpunit/data/media/test.jpgbin0 -> 437 bytes
-rw-r--r--tests/phpunit/data/media/test.tiffbin0 -> 566 bytes
-rw-r--r--tests/phpunit/data/media/xmp.pngbin0 -> 582 bytes
-rw-r--r--tests/phpunit/data/xmp/1.result.php8
-rw-r--r--tests/phpunit/data/xmp/1.xmp11
-rw-r--r--tests/phpunit/data/xmp/2.result.php8
-rw-r--r--tests/phpunit/data/xmp/2.xmp12
-rw-r--r--tests/phpunit/data/xmp/3-invalid.result.php7
-rw-r--r--tests/phpunit/data/xmp/3-invalid.xmp31
-rw-r--r--tests/phpunit/data/xmp/3.result.php8
-rw-r--r--tests/phpunit/data/xmp/3.xmp29
-rw-r--r--tests/phpunit/data/xmp/4.result.php7
-rw-r--r--tests/phpunit/data/xmp/4.xmp22
-rw-r--r--tests/phpunit/data/xmp/5.result.php7
-rw-r--r--tests/phpunit/data/xmp/5.xmp16
-rw-r--r--tests/phpunit/data/xmp/6.result.php8
-rw-r--r--tests/phpunit/data/xmp/6.xmp18
-rw-r--r--tests/phpunit/data/xmp/7.result.php52
-rw-r--r--tests/phpunit/data/xmp/7.xmp67
-rw-r--r--tests/phpunit/data/xmp/README3
-rw-r--r--tests/phpunit/data/xmp/bag-for-seq.result.php10
-rw-r--r--tests/phpunit/data/xmp/bag-for-seq.xmp1
-rw-r--r--tests/phpunit/data/xmp/flash.result.php8
-rw-r--r--tests/phpunit/data/xmp/flash.xmp11
-rw-r--r--tests/phpunit/data/xmp/invalid-child-not-struct.result.php7
-rw-r--r--tests/phpunit/data/xmp/invalid-child-not-struct.xmp12
-rw-r--r--tests/phpunit/data/xmp/no-namespace.result.php7
-rw-r--r--tests/phpunit/data/xmp/no-namespace.xmp11
-rw-r--r--tests/phpunit/data/xmp/no-recognized-props.result.php2
-rw-r--r--tests/phpunit/data/xmp/no-recognized-props.xmp8
-rw-r--r--tests/phpunit/data/xmp/utf16BE.result.php12
-rw-r--r--tests/phpunit/data/xmp/utf16BE.xmpbin0 -> 930 bytes
-rw-r--r--tests/phpunit/data/xmp/utf16LE.result.php12
-rw-r--r--tests/phpunit/data/xmp/utf16LE.xmpbin0 -> 930 bytes
-rw-r--r--tests/phpunit/data/xmp/utf32BE.result.php12
-rw-r--r--tests/phpunit/data/xmp/utf32BE.xmpbin0 -> 1856 bytes
-rw-r--r--tests/phpunit/data/xmp/utf32LE.result.php12
-rw-r--r--tests/phpunit/data/xmp/utf32LE.xmpbin0 -> 1856 bytes
-rw-r--r--tests/phpunit/data/xmp/xmpExt.result.php8
-rw-r--r--tests/phpunit/data/xmp/xmpExt.xmp13
-rw-r--r--tests/phpunit/data/xmp/xmpExt2.xmp8
-rw-r--r--tests/phpunit/data/zip/cd-gap.zipbin0 -> 182 bytes
-rw-r--r--tests/phpunit/data/zip/cd-truncated.zipbin0 -> 171 bytes
-rw-r--r--tests/phpunit/data/zip/class-trailing-null.zipbin0 -> 173 bytes
-rw-r--r--tests/phpunit/data/zip/class-trailing-slash.zipbin0 -> 173 bytes
-rw-r--r--tests/phpunit/data/zip/class.zipbin0 -> 173 bytes
-rw-r--r--tests/phpunit/data/zip/empty.zipbin0 -> 22 bytes
-rw-r--r--tests/phpunit/data/zip/looks-like-zip64.zipbin0 -> 173 bytes
-rw-r--r--tests/phpunit/data/zip/nosig.zipbin0 -> 173 bytes
-rw-r--r--tests/phpunit/data/zip/split.zipbin0 -> 196 bytes
-rw-r--r--tests/phpunit/data/zip/trail.zipbin0 -> 181 bytes
-rw-r--r--tests/phpunit/data/zip/wrong-cd-start-disk.zipbin0 -> 173 bytes
-rw-r--r--tests/phpunit/data/zip/wrong-central-entry-sig.zipbin0 -> 173 bytes
-rw-r--r--tests/phpunit/includes/ArticleTablesTest.php34
-rw-r--r--tests/phpunit/includes/ArticleTest.php82
-rw-r--r--tests/phpunit/includes/BlockTest.php124
-rw-r--r--tests/phpunit/includes/CdbTest.php84
-rw-r--r--tests/phpunit/includes/ExternalStoreTest.php32
-rw-r--r--tests/phpunit/includes/ExtraParserTest.php113
-rw-r--r--tests/phpunit/includes/FauxResponseTest.php70
-rw-r--r--tests/phpunit/includes/FormOptionsInitializationTest.php85
-rw-r--r--tests/phpunit/includes/FormOptionsTest.php90
-rw-r--r--tests/phpunit/includes/GlobalFunctions/GlobalTest.php902
-rw-r--r--tests/phpunit/includes/GlobalFunctions/README2
-rw-r--r--tests/phpunit/includes/GlobalFunctions/wfExpandUrl.php78
-rw-r--r--tests/phpunit/includes/GlobalFunctions/wfUrlencodeTest.php120
-rw-r--r--tests/phpunit/includes/HooksTest.php102
-rw-r--r--tests/phpunit/includes/HtmlTest.php90
-rw-r--r--tests/phpunit/includes/HttpTest.php618
-rw-r--r--tests/phpunit/includes/IPTest.php508
-rw-r--r--tests/phpunit/includes/ImageFunctionsTest.php48
-rw-r--r--tests/phpunit/includes/JsonTest.php33
-rw-r--r--tests/phpunit/includes/LanguageConverterTest.php130
-rw-r--r--tests/phpunit/includes/LicensesTest.php22
-rw-r--r--tests/phpunit/includes/LocalFileTest.php99
-rw-r--r--tests/phpunit/includes/MWFunctionTest.php86
-rw-r--r--tests/phpunit/includes/MWNamespaceTest.php460
-rw-r--r--tests/phpunit/includes/MessageTest.php62
-rw-r--r--tests/phpunit/includes/ParserOptionsTest.php36
-rw-r--r--tests/phpunit/includes/Providers.php44
-rw-r--r--tests/phpunit/includes/ResourceLoaderTest.php91
-rw-r--r--tests/phpunit/includes/RevisionTest.php125
-rw-r--r--tests/phpunit/includes/SampleTest.php98
-rw-r--r--tests/phpunit/includes/SanitizerTest.php113
-rw-r--r--tests/phpunit/includes/SeleniumConfigurationTest.php228
-rw-r--r--tests/phpunit/includes/SiteConfigurationTest.php311
-rw-r--r--tests/phpunit/includes/TimeAdjustTest.php51
-rw-r--r--tests/phpunit/includes/TitlePermissionTest.php654
-rw-r--r--tests/phpunit/includes/TitleTest.php79
-rw-r--r--tests/phpunit/includes/UserIsValidEmailAddrTest.php79
-rw-r--r--tests/phpunit/includes/UserTest.php58
-rw-r--r--tests/phpunit/includes/WebRequestTest.php88
-rw-r--r--tests/phpunit/includes/XmlJsTest.php9
-rw-r--r--tests/phpunit/includes/XmlSelectTest.php139
-rw-r--r--tests/phpunit/includes/XmlTest.php304
-rw-r--r--tests/phpunit/includes/ZipDirectoryReaderTest.php79
-rw-r--r--tests/phpunit/includes/api/ApiBlockTest.php62
-rw-r--r--tests/phpunit/includes/api/ApiPurgeTest.php41
-rw-r--r--tests/phpunit/includes/api/ApiQueryTest.php67
-rw-r--r--tests/phpunit/includes/api/ApiTest.php277
-rw-r--r--tests/phpunit/includes/api/ApiTestCase.php139
-rw-r--r--tests/phpunit/includes/api/ApiTestCaseUpload.php114
-rw-r--r--tests/phpunit/includes/api/ApiTestUser.php59
-rw-r--r--tests/phpunit/includes/api/ApiUploadTest.php433
-rw-r--r--tests/phpunit/includes/api/ApiWatchTest.php179
-rw-r--r--tests/phpunit/includes/api/RandomImageGenerator.php473
-rw-r--r--tests/phpunit/includes/api/format/ApiFormatPhpTest.php19
-rw-r--r--tests/phpunit/includes/api/format/ApiFormatTestBase.php22
-rw-r--r--tests/phpunit/includes/api/generateRandomImages.php47
-rw-r--r--tests/phpunit/includes/api/words.txt1000
-rw-r--r--tests/phpunit/includes/db/DatabaseSqliteTest.php312
-rw-r--r--tests/phpunit/includes/db/DatabaseTest.php95
-rw-r--r--tests/phpunit/includes/db/sqlite/tables-1.13.sql342
-rw-r--r--tests/phpunit/includes/db/sqlite/tables-1.15.sql454
-rw-r--r--tests/phpunit/includes/db/sqlite/tables-1.16.sql483
-rw-r--r--tests/phpunit/includes/db/sqlite/tables-1.17.sql516
-rw-r--r--tests/phpunit/includes/installer/InstallDocFormatterTest.php64
-rw-r--r--tests/phpunit/includes/libs/IEUrlExtensionTest.php118
-rw-r--r--tests/phpunit/includes/libs/JavaScriptMinifierTest.php105
-rw-r--r--tests/phpunit/includes/media/BitmapMetadataHandlerTest.php125
-rw-r--r--tests/phpunit/includes/media/BitmapScalingTest.php136
-rw-r--r--tests/phpunit/includes/media/ExifBitmapTest.php122
-rw-r--r--tests/phpunit/includes/media/ExifRotationTest.php249
-rw-r--r--tests/phpunit/includes/media/ExifTest.php51
-rw-r--r--tests/phpunit/includes/media/FormatMetadataTest.php29
-rw-r--r--tests/phpunit/includes/media/GIFMetadataExtractorTest.php95
-rw-r--r--tests/phpunit/includes/media/GIFTest.php85
-rw-r--r--tests/phpunit/includes/media/IPTCTest.php55
-rw-r--r--tests/phpunit/includes/media/JpegMetadataExtractorTest.php94
-rw-r--r--tests/phpunit/includes/media/JpegTest.php29
-rw-r--r--tests/phpunit/includes/media/PNGMetadataExtractorTest.php141
-rw-r--r--tests/phpunit/includes/media/PNGTest.php88
-rw-r--r--tests/phpunit/includes/media/SVGMetadataExtractorTest.php88
-rw-r--r--tests/phpunit/includes/media/TiffTest.php36
-rw-r--r--tests/phpunit/includes/media/XMPTest.php154
-rw-r--r--tests/phpunit/includes/normal/CleanUpTest.php382
-rw-r--r--tests/phpunit/includes/parser/MagicVariableTest.php195
-rw-r--r--tests/phpunit/includes/parser/MediaWikiParserTest.php34
-rw-r--r--tests/phpunit/includes/parser/NewParserTest.php850
-rw-r--r--tests/phpunit/includes/parser/ParserHelpers.php136
-rw-r--r--tests/phpunit/includes/parser/PreprocessorTest.php195
-rw-r--r--tests/phpunit/includes/parser/TagHooks.php77
-rw-r--r--tests/phpunit/includes/search/SearchEngineTest.php163
-rw-r--r--tests/phpunit/includes/search/SearchUpdateTest.php80
-rw-r--r--tests/phpunit/includes/specials/SpecialRecentchanges.php134
-rw-r--r--tests/phpunit/includes/upload/UploadFromUrlTest.php348
-rw-r--r--tests/phpunit/includes/upload/UploadStashTest.php53
-rw-r--r--tests/phpunit/includes/upload/UploadTest.php142
-rw-r--r--tests/phpunit/install-phpunit.sh36
-rw-r--r--tests/phpunit/languages/LanguageBe_taraskTest.php30
-rw-r--r--tests/phpunit/languages/LanguageTest.php246
-rw-r--r--tests/phpunit/languages/LanguageTrTest.php65
-rw-r--r--tests/phpunit/phpunit.php61
-rw-r--r--tests/phpunit/run-tests.bat1
-rw-r--r--tests/phpunit/skins/SideBarTest.php175
-rw-r--r--tests/phpunit/suite.xml37
-rw-r--r--tests/phpunit/suites/ExtensionsTestSuite.php33
-rw-r--r--tests/phpunit/suites/UploadFromUrlTestSuite.php178
-rw-r--r--tests/qunit/.htaccess1
-rw-r--r--tests/qunit/data/defineTestCallback.js4
-rw-r--r--tests/qunit/data/testrunner.js92
-rw-r--r--tests/qunit/data/testwarm.inject.js349
-rw-r--r--tests/qunit/index.html90
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js58
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.byteLength.js42
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.byteLimit.js155
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.client.js205
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.colorUtil.js71
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.getAttrs.js17
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.localize.js119
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.mwPrototypes.js56
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.tabIndex.js50
-rw-r--r--tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js475
-rw-r--r--tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.js71
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.js232
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js35
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.user.js29
-rw-r--r--tests/qunit/suites/resources/mediawiki/mediawiki.util.js307
-rw-r--r--tests/selenium/Selenium.php190
-rw-r--r--tests/selenium/SeleniumConfig.php113
-rw-r--r--tests/selenium/SeleniumLoader.php9
-rw-r--r--tests/selenium/SeleniumServerManager.php239
-rw-r--r--tests/selenium/SeleniumTestCase.php127
-rw-r--r--tests/selenium/SeleniumTestConsoleLogger.php25
-rw-r--r--tests/selenium/SeleniumTestConstants.php24
-rw-r--r--tests/selenium/SeleniumTestHTMLLogger.php36
-rw-r--r--tests/selenium/SeleniumTestListener.php68
-rw-r--r--tests/selenium/SeleniumTestSuite.php57
-rw-r--r--tests/selenium/data/SimpleSeleniumTestDB.sql1454
-rw-r--r--tests/selenium/data/SimpleSeleniumTestImages.zipbin0 -> 21993 bytes
-rw-r--r--tests/selenium/data/Wikipedia-logo-v2-de.pngbin0 -> 21479 bytes
-rw-r--r--tests/selenium/data/mediawiki118_fresh_installation.sql1544
-rw-r--r--tests/selenium/installer/MediaWikiButtonsAvailabilityTestCase.php102
-rw-r--r--tests/selenium/installer/MediaWikiDifferentDatabaseAccountTestCase.php82
-rw-r--r--tests/selenium/installer/MediaWikiDifferntDatabasePrefixTestCase.php95
-rw-r--r--tests/selenium/installer/MediaWikiErrorsConnectToDatabasePageTestCase.php136
-rw-r--r--tests/selenium/installer/MediaWikiErrorsNamepageTestCase.php132
-rw-r--r--tests/selenium/installer/MediaWikiHelpFieldHintTestCase.php140
-rw-r--r--tests/selenium/installer/MediaWikiInstallationCommonFunction.php283
-rw-r--r--tests/selenium/installer/MediaWikiInstallationConfig.php49
-rw-r--r--tests/selenium/installer/MediaWikiInstallationMessage.php57
-rw-r--r--tests/selenium/installer/MediaWikiInstallationVariables.php77
-rw-r--r--tests/selenium/installer/MediaWikiInstallerTestSuite.php53
-rw-r--r--tests/selenium/installer/MediaWikiMySQLDataBaseTestCase.php78
-rw-r--r--tests/selenium/installer/MediaWikiMySQLiteDataBaseTestCase.php79
-rw-r--r--tests/selenium/installer/MediaWikiOnAlreadyInstalledTestCase.php71
-rw-r--r--tests/selenium/installer/MediaWikiRestartInstallationTestCase.php115
-rw-r--r--tests/selenium/installer/MediaWikiRightFrameworkLinksTestCase.php93
-rw-r--r--tests/selenium/installer/MediaWikiUpgradeExistingDatabaseTestCase.php117
-rw-r--r--tests/selenium/installer/MediaWikiUserInterfaceTestCase.php531
-rw-r--r--tests/selenium/installer/README.txt32
-rw-r--r--tests/selenium/selenium_settings.ini.sample32
-rw-r--r--tests/selenium/selenium_settings_grid.ini.sample16
-rw-r--r--tests/selenium/suites/AddContentToNewPageTestCase.php182
-rw-r--r--tests/selenium/suites/AddNewPageTestCase.php65
-rw-r--r--tests/selenium/suites/CreateAccountTestCase.php114
-rw-r--r--tests/selenium/suites/DeletePageAdminTestCase.php89
-rw-r--r--tests/selenium/suites/EmailPasswordTestCase.php81
-rw-r--r--tests/selenium/suites/MediaWikiEditorConfig.php48
-rw-r--r--tests/selenium/suites/MediaWikiEditorTestSuite.php18
-rw-r--r--tests/selenium/suites/MediaWikiExtraTestSuite.php20
-rw-r--r--tests/selenium/suites/MediawikiCoreSmokeTestCase.php69
-rw-r--r--tests/selenium/suites/MediawikiCoreSmokeTestSuite.php19
-rw-r--r--tests/selenium/suites/MovePageTestCase.php117
-rw-r--r--tests/selenium/suites/MyContributionsTestCase.php65
-rw-r--r--tests/selenium/suites/MyWatchListTestCase.php57
-rw-r--r--tests/selenium/suites/PageDeleteTestSuite.php16
-rw-r--r--tests/selenium/suites/PageSearchTestCase.php105
-rw-r--r--tests/selenium/suites/PreviewPageTestCase.php53
-rw-r--r--tests/selenium/suites/SavePageTestCase.php58
-rw-r--r--tests/selenium/suites/SimpleSeleniumConfig.php30
-rw-r--r--tests/selenium/suites/SimpleSeleniumTestCase.php39
-rw-r--r--tests/selenium/suites/SimpleSeleniumTestSuite.php26
-rw-r--r--tests/selenium/suites/UserPreferencesTestCase.php179
-rw-r--r--tests/testHelpers.inc536
293 files changed, 51623 insertions, 0 deletions
diff --git a/tests/.htaccess b/tests/.htaccess
new file mode 100644
index 00000000..3a428827
--- /dev/null
+++ b/tests/.htaccess
@@ -0,0 +1 @@
+Deny from all
diff --git a/tests/RunSeleniumTests.php b/tests/RunSeleniumTests.php
new file mode 100644
index 00000000..9cadd759
--- /dev/null
+++ b/tests/RunSeleniumTests.php
@@ -0,0 +1,233 @@
+#!/usr/bin/php
+<?php
+/**
+ * @file
+ * @ingroup Maintenance
+ * @copyright Copyright © Wikimedia Deuschland, 2009
+ * @author Hallo Welt! Medienwerkstatt GmbH
+ * @author Markus Glaser, Dan Nessett, Priyanka Dhanda
+ * initial idea by Daniel Kinzler
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+$IP = dirname( dirname( __FILE__ ) );
+
+define( 'SELENIUMTEST', true );
+
+//require_once( dirname( __FILE__ ) . '/../maintenance/commandLine.inc' );
+require( dirname( __FILE__ ) . '/../maintenance/Maintenance.php' );
+
+require_once( 'PHPUnit/Runner/Version.php' );
+if( version_compare( PHPUnit_Runner_Version::id(), '3.5.0', '>=' ) ) {
+ # PHPUnit 3.5.0 introduced a nice autoloader based on class name
+ require_once( 'PHPUnit/Autoload.php' );
+} else {
+ # Keep the old pre PHPUnit 3.5.0 behaviour for compatibility
+ require_once( 'PHPUnit/TextUI/Command.php' );
+}
+
+require_once( 'PHPUnit/Extensions/SeleniumTestCase.php' );
+include_once( 'PHPUnit/Util/Log/JUnit.php' );
+
+require_once( dirname( __FILE__ ) . "/selenium/SeleniumServerManager.php" );
+
+class SeleniumTester extends Maintenance {
+ protected $selenium;
+ protected $serverManager;
+ protected $seleniumServerExecPath;
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Selenium Test Runner. For documentation, visit http://www.mediawiki.org/wiki/SeleniumFramework";
+ $this->addOption( 'port', 'Port used by selenium server. Default: 4444', false, true );
+ $this->addOption( 'host', 'Host selenium server. Default: $wgServer . $wgScriptPath', false, true );
+ $this->addOption( 'testBrowser', 'The browser used during testing. Default: firefox', false, true );
+ $this->addOption( 'wikiUrl', 'The Mediawiki installation to point to. Default: http://localhost', false, true );
+ $this->addOption( 'username', 'The login username for sunning tests. Default: empty', false, true );
+ $this->addOption( 'userPassword', 'The login password for running tests. Default: empty', false, true );
+ $this->addOption( 'seleniumConfig', 'Location of the selenium config file. Default: empty', false, true );
+ $this->addOption( 'list-browsers', 'List the available browsers.' );
+ $this->addOption( 'verbose', 'Be noisier.' );
+ $this->addOption( 'startserver', 'Start Selenium Server (on localhost) before the run.' );
+ $this->addOption( 'stopserver', 'Stop Selenium Server (on localhost) after the run.' );
+ $this->addOption( 'jUnitLogFile', 'Log results in a specified JUnit log file. Default: empty', false, true );
+ $this->addOption( 'runAgainstGrid', 'The test will be run against a Selenium Grid. Default: false.', false, true );
+ $this->deleteOption( 'dbpass' );
+ $this->deleteOption( 'dbuser' );
+ $this->deleteOption( 'globals' );
+ $this->deleteOption( 'wiki' );
+ }
+
+ public function listBrowsers() {
+ $desc = "Available browsers:\n";
+
+ foreach ($this->selenium->getAvailableBrowsers() as $k => $v) {
+ $desc .= " $k => $v\n";
+ }
+
+ echo $desc;
+ }
+
+ protected function startServer() {
+ if ( $this->seleniumServerExecPath == '' ) {
+ die ( "The selenium server exec path is not set in " .
+ "selenium_settings.ini. Cannot start server \n" .
+ "as requested - terminating RunSeleniumTests\n" );
+ }
+ $this->serverManager = new SeleniumServerManager( 'true',
+ $this->selenium->getPort(),
+ $this->seleniumServerExecPath );
+ switch ( $this->serverManager->start() ) {
+ case 'started':
+ break;
+ case 'failed':
+ die ( "Unable to start the Selenium Server - " .
+ "terminating RunSeleniumTests\n" );
+ case 'running':
+ echo ( "Warning: The Selenium Server is " .
+ "already running\n" );
+ break;
+ }
+
+ return;
+ }
+
+ protected function stopServer() {
+ if ( !isset ( $this->serverManager ) ) {
+ echo ( "Warning: Request to stop Selenium Server, but it was " .
+ "not stared by RunSeleniumTests\n" .
+ "RunSeleniumTests cannot stop a Selenium Server it " .
+ "did not start\n" );
+ } else {
+ switch ( $this->serverManager->stop() ) {
+ case 'stopped':
+ break;
+ case 'failed':
+ echo ( "unable to stop the Selenium Server\n" );
+ }
+ }
+ return;
+ }
+
+ protected function runTests( $seleniumTestSuites = array() ) {
+ $result = new PHPUnit_Framework_TestResult;
+ $result->addListener( new SeleniumTestListener( $this->selenium->getLogger() ) );
+ if ( $this->selenium->getJUnitLogFile() ) {
+ $jUnitListener = new PHPUnit_Util_Log_JUnit( $this->selenium->getJUnitLogFile(), true );
+ $result->addListener( $jUnitListener );
+ }
+
+ foreach ( $seleniumTestSuites as $testSuiteName => $testSuiteFile ) {
+ require( $testSuiteFile );
+ $suite = new $testSuiteName();
+ $suite->setName( $testSuiteName );
+ $suite->addTests();
+
+ try {
+ $suite->run( $result );
+ } catch ( Testing_Selenium_Exception $e ) {
+ $suite->tearDown();
+ throw new MWException( $e->getMessage() );
+ }
+ }
+
+ if ( $this->selenium->getJUnitLogFile() ) {
+ $jUnitListener->flush();
+ }
+ }
+
+ public function execute() {
+ global $wgServer, $wgScriptPath, $wgHooks;
+
+ $seleniumSettings = array();
+ $seleniumBrowsers = array();
+ $seleniumTestSuites = array();
+
+ $configFile = $this->getOption( 'seleniumConfig', '' );
+ if ( strlen( $configFile ) > 0 ) {
+ $this->output("Using Selenium Configuration file: " . $configFile . "\n");
+ SeleniumConfig::getSeleniumSettings( $seleniumSettings,
+ $seleniumBrowsers,
+ $seleniumTestSuites,
+ $configFile );
+ } elseif ( !isset( $wgHooks['SeleniumSettings'] ) ) {
+ $this->output("No command line, configuration file or configuration hook found.\n");
+ SeleniumConfig::getSeleniumSettings( $seleniumSettings,
+ $seleniumBrowsers,
+ $seleniumTestSuites
+ );
+ } else {
+ $this->output("Using 'SeleniumSettings' hook for configuration.\n");
+ wfRunHooks('SeleniumSettings', array( $seleniumSettings,
+ $seleniumBrowsers,
+ $seleniumTestSuites ) );
+ }
+
+ // State for starting/stopping the Selenium server has nothing to do with the Selenium
+ // class. Keep this state local to SeleniumTester class. Using getOption() is clumsy, but
+ // the Maintenance class does not have a setOption()
+ if ( ! isset( $seleniumSettings['startserver'] ) ) $this->getOption( 'startserver', true );
+ if ( ! isset( $seleniumSettings['stopserver'] ) ) $this->getOption( 'stopserver', true );
+ if ( !isset( $seleniumSettings['seleniumserverexecpath'] ) ) $seleniumSettings['seleniumserverexecpath'] = '';
+ $this->seleniumServerExecPath = $seleniumSettings['seleniumserverexecpath'];
+
+ //set reasonable defaults if we did not find the settings
+ if ( !isset( $seleniumBrowsers ) ) $seleniumBrowsers = array ('firefox' => '*firefox');
+ if ( !isset( $seleniumSettings['host'] ) ) $seleniumSettings['host'] = $wgServer . $wgScriptPath;
+ if ( !isset( $seleniumSettings['port'] ) ) $seleniumSettings['port'] = '4444';
+ if ( !isset( $seleniumSettings['wikiUrl'] ) ) $seleniumSettings['wikiUrl'] = 'http://localhost';
+ if ( !isset( $seleniumSettings['username'] ) ) $seleniumSettings['username'] = '';
+ if ( !isset( $seleniumSettings['userPassword'] ) ) $seleniumSettings['userPassword'] = '';
+ if ( !isset( $seleniumSettings['testBrowser'] ) ) $seleniumSettings['testBrowser'] = 'firefox';
+ if ( !isset( $seleniumSettings['jUnitLogFile'] ) ) $seleniumSettings['jUnitLogFile'] = false;
+ if ( !isset( $seleniumSettings['runAgainstGrid'] ) ) $seleniumSettings['runAgainstGrid'] = false;
+
+ // Setup Selenium class
+ $this->selenium = new Selenium( );
+ $this->selenium->setAvailableBrowsers( $seleniumBrowsers );
+ $this->selenium->setRunAgainstGrid( $this->getOption( 'runAgainstGrid', $seleniumSettings['runAgainstGrid'] ) );
+ $this->selenium->setUrl( $this->getOption( 'wikiUrl', $seleniumSettings['wikiUrl'] ) );
+ $this->selenium->setBrowser( $this->getOption( 'testBrowser', $seleniumSettings['testBrowser'] ) );
+ $this->selenium->setPort( $this->getOption( 'port', $seleniumSettings['port'] ) );
+ $this->selenium->setHost( $this->getOption( 'host', $seleniumSettings['host'] ) );
+ $this->selenium->setUser( $this->getOption( 'username', $seleniumSettings['username'] ) );
+ $this->selenium->setPass( $this->getOption( 'userPassword', $seleniumSettings['userPassword'] ) );
+ $this->selenium->setVerbose( $this->hasOption( 'verbose' ) );
+ $this->selenium->setJUnitLogFile( $this->getOption( 'jUnitLogFile', $seleniumSettings['jUnitLogFile'] ) );
+
+ if( $this->hasOption( 'list-browsers' ) ) {
+ $this->listBrowsers();
+ exit(0);
+ }
+ if ( $this->hasOption( 'startserver' ) ) {
+ $this->startServer();
+ }
+
+ $logger = new SeleniumTestConsoleLogger;
+ $this->selenium->setLogger( $logger );
+
+ $this->runTests( $seleniumTestSuites );
+
+ if ( $this->hasOption( 'stopserver' ) ) {
+ $this->stopServer();
+ }
+ }
+}
+
+$maintClass = "SeleniumTester";
+
+require_once( RUN_MAINTENANCE_IF_MAIN );
diff --git a/tests/TestsAutoLoader.php b/tests/TestsAutoLoader.php
new file mode 100644
index 00000000..cccf7bf2
--- /dev/null
+++ b/tests/TestsAutoLoader.php
@@ -0,0 +1,33 @@
+<?php
+
+global $wgAutoloadClasses;
+$testFolder = dirname( __FILE__ );
+
+$wgAutoloadClasses += array(
+
+ //PHPUnit
+ 'MediaWikiTestCase' => "$testFolder/phpunit/MediaWikiTestCase.php",
+ 'MediaWikiPHPUnitCommand' => "$testFolder/phpunit/MediaWikiPHPUnitCommand.php",
+ 'MediaWikiLangTestCase' => "$testFolder/phpunit/MediaWikiLangTestCase.php",
+
+ //includes
+ 'BlockTest' => "$testFolder/phpunit/includes/BlockTest.php",
+
+ //API
+ 'ApiFormatTestBase' => "$testFolder/phpunit/includes/api/format/ApiFormatTestBase.php",
+ 'ApiTestCase' => "$testFolder/phpunit/includes/api/ApiTestCase.php",
+ 'ApiTestUser' => "$testFolder/phpunit/includes/api/ApiTestUser.php",
+ 'MockApi' => "$testFolder/phpunit/includes/api/ApiTestCase.php",
+ 'RandomImageGenerator' => "$testFolder/phpunit/includes/api/RandomImageGenerator.php",
+ 'UserWrapper' => "$testFolder/phpunit/includes/api/ApiTestCase.php",
+
+ //Parser
+ 'ParserTestFileIterator' => "$testFolder/phpunit/includes/parser/NewParserHelpers.php",
+
+ //Selenium
+ 'SeleniumTestConstants' => "$testFolder/selenium/SeleniumTestConstants.php",
+
+ //Generic providers
+ 'MediaWikiProvide' => "$testFolder/phpunit/includes/Providers.php",
+);
+
diff --git a/tests/parser/extraParserTests.txt b/tests/parser/extraParserTests.txt
new file mode 100644
index 00000000..bef8f506
--- /dev/null
+++ b/tests/parser/extraParserTests.txt
Binary files differ
diff --git a/tests/parser/parserTest.inc b/tests/parser/parserTest.inc
new file mode 100644
index 00000000..4b4c7f72
--- /dev/null
+++ b/tests/parser/parserTest.inc
@@ -0,0 +1,1280 @@
+<?php
+# Copyright (C) 2004, 2010 Brion Vibber <brion@pobox.com>
+# http://www.mediawiki.org/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# http://www.gnu.org/copyleft/gpl.html
+
+/**
+ * @todo Make this more independent of the configuration (and if possible the database)
+ * @todo document
+ * @file
+ * @ingroup Testing
+ */
+
+/**
+ * @ingroup Testing
+ */
+class ParserTest {
+ /**
+ * boolean $color whereas output should be colorized
+ */
+ private $color;
+
+ /**
+ * boolean $showOutput Show test output
+ */
+ private $showOutput;
+
+ /**
+ * boolean $useTemporaryTables Use temporary tables for the temporary database
+ */
+ private $useTemporaryTables = true;
+
+ /**
+ * boolean $databaseSetupDone True if the database has been set up
+ */
+ private $databaseSetupDone = false;
+
+ /**
+ * Our connection to the database
+ * @var DatabaseBase
+ */
+ private $db;
+
+ /**
+ * Database clone helper
+ * @var CloneDatabase
+ */
+ private $dbClone;
+
+ /**
+ * string $oldTablePrefix Original table prefix
+ */
+ private $oldTablePrefix;
+
+ private $maxFuzzTestLength = 300;
+ private $fuzzSeed = 0;
+ private $memoryLimit = 50;
+ private $uploadDir = null;
+
+ public $regex = "";
+ private $savedGlobals = array();
+ /**
+ * Sets terminal colorization and diff/quick modes depending on OS and
+ * command-line options (--color and --quick).
+ */
+ public function __construct( $options = array() ) {
+ # Only colorize output if stdout is a terminal.
+ $this->color = !wfIsWindows() && posix_isatty( 1 );
+
+ if ( isset( $options['color'] ) ) {
+ switch( $options['color'] ) {
+ case 'no':
+ $this->color = false;
+ break;
+ case 'yes':
+ default:
+ $this->color = true;
+ break;
+ }
+ }
+
+ $this->term = $this->color
+ ? new AnsiTermColorer()
+ : new DummyTermColorer();
+
+ $this->showDiffs = !isset( $options['quick'] );
+ $this->showProgress = !isset( $options['quiet'] );
+ $this->showFailure = !(
+ isset( $options['quiet'] )
+ && ( isset( $options['record'] )
+ || isset( $options['compare'] ) ) ); // redundant output
+
+ $this->showOutput = isset( $options['show-output'] );
+
+
+ if ( isset( $options['regex'] ) ) {
+ if ( isset( $options['record'] ) ) {
+ echo "Warning: --record cannot be used with --regex, disabling --record\n";
+ unset( $options['record'] );
+ }
+ $this->regex = $options['regex'];
+ } else {
+ # Matches anything
+ $this->regex = '';
+ }
+
+ $this->setupRecorder( $options );
+ $this->keepUploads = isset( $options['keep-uploads'] );
+
+ if ( isset( $options['seed'] ) ) {
+ $this->fuzzSeed = intval( $options['seed'] ) - 1;
+ }
+
+ $this->runDisabled = isset( $options['run-disabled'] );
+
+ $this->hooks = array();
+ $this->functionHooks = array();
+ self::setUp();
+ }
+
+ static function setUp() {
+ global $wgParser, $wgParserConf, $IP, $messageMemc, $wgMemc, $wgDeferredUpdateList,
+ $wgUser, $wgLang, $wgOut, $wgRequest, $wgStyleDirectory, $wgEnableParserCache,
+ $wgNamespaceAliases, $wgNamespaceProtection, $wgLocalFileRepo,
+ $parserMemc, $wgThumbnailScriptPath, $wgScriptPath,
+ $wgArticlePath, $wgStyleSheetPath, $wgScript, $wgStylePath;
+
+ $wgScript = '/index.php';
+ $wgScriptPath = '/';
+ $wgArticlePath = '/wiki/$1';
+ $wgStyleSheetPath = '/skins';
+ $wgStylePath = '/skins';
+ $wgThumbnailScriptPath = false;
+ $wgLocalFileRepo = array(
+ 'class' => 'LocalRepo',
+ 'name' => 'local',
+ 'directory' => wfTempDir() . '/test-repo',
+ 'url' => 'http://example.com/images',
+ 'deletedDir' => wfTempDir() . '/test-repo/delete',
+ 'hashLevels' => 2,
+ 'transformVia404' => false,
+ );
+ $wgNamespaceProtection[NS_MEDIAWIKI] = 'editinterface';
+ $wgNamespaceAliases['Image'] = NS_FILE;
+ $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK;
+
+
+ $wgEnableParserCache = false;
+ $wgDeferredUpdateList = array();
+ $wgMemc = wfGetMainCache();
+ $messageMemc = wfGetMessageCacheStorage();
+ $parserMemc = wfGetParserCacheStorage();
+
+ // $wgContLang = new StubContLang;
+ $wgUser = new User;
+ $context = new RequestContext;
+ $wgLang = $context->getLang();
+ $wgOut = $context->getOutput();
+ $wgParser = new StubObject( 'wgParser', $wgParserConf['class'], array( $wgParserConf ) );
+ $wgRequest = new WebRequest;
+
+ if ( $wgStyleDirectory === false ) {
+ $wgStyleDirectory = "$IP/skins";
+ }
+
+ }
+
+ public function setupRecorder ( $options ) {
+ if ( isset( $options['record'] ) ) {
+ $this->recorder = new DbTestRecorder( $this );
+ $this->recorder->version = isset( $options['setversion'] ) ?
+ $options['setversion'] : SpecialVersion::getVersion();
+ } elseif ( isset( $options['compare'] ) ) {
+ $this->recorder = new DbTestPreviewer( $this );
+ } else {
+ $this->recorder = new TestRecorder( $this );
+ }
+ }
+
+ /**
+ * Remove last character if it is a newline
+ * @group utility
+ */
+ static public function chomp( $s ) {
+ if ( substr( $s, -1 ) === "\n" ) {
+ return substr( $s, 0, -1 );
+ }
+ else {
+ return $s;
+ }
+ }
+
+ /**
+ * Run a fuzz test series
+ * Draw input from a set of test files
+ */
+ function fuzzTest( $filenames ) {
+ $GLOBALS['wgContLang'] = Language::factory( 'en' );
+ $dict = $this->getFuzzInput( $filenames );
+ $dictSize = strlen( $dict );
+ $logMaxLength = log( $this->maxFuzzTestLength );
+ $this->setupDatabase();
+ ini_set( 'memory_limit', $this->memoryLimit * 1048576 );
+
+ $numTotal = 0;
+ $numSuccess = 0;
+ $user = new User;
+ $opts = ParserOptions::newFromUser( $user );
+ $title = Title::makeTitle( NS_MAIN, 'Parser_test' );
+
+ while ( true ) {
+ // Generate test input
+ mt_srand( ++$this->fuzzSeed );
+ $totalLength = mt_rand( 1, $this->maxFuzzTestLength );
+ $input = '';
+
+ while ( strlen( $input ) < $totalLength ) {
+ $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength;
+ $hairLength = min( intval( exp( $logHairLength ) ), $dictSize );
+ $offset = mt_rand( 0, $dictSize - $hairLength );
+ $input .= substr( $dict, $offset, $hairLength );
+ }
+
+ $this->setupGlobals();
+ $parser = $this->getParser();
+
+ // Run the test
+ try {
+ $parser->parse( $input, $title, $opts );
+ $fail = false;
+ } catch ( Exception $exception ) {
+ $fail = true;
+ }
+
+ if ( $fail ) {
+ echo "Test failed with seed {$this->fuzzSeed}\n";
+ echo "Input:\n";
+ printf( "string(%d) \"%s\"\n\n", strlen( $input ), $input );
+ echo "$exception\n";
+ } else {
+ $numSuccess++;
+ }
+
+ $numTotal++;
+ $this->teardownGlobals();
+ $parser->__destruct();
+
+ if ( $numTotal % 100 == 0 ) {
+ $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 );
+ echo "{$this->fuzzSeed}: $numSuccess/$numTotal (mem: $usage%)\n";
+ if ( $usage > 90 ) {
+ echo "Out of memory:\n";
+ $memStats = $this->getMemoryBreakdown();
+
+ foreach ( $memStats as $name => $usage ) {
+ echo "$name: $usage\n";
+ }
+ $this->abort();
+ }
+ }
+ }
+ }
+
+ /**
+ * Get an input dictionary from a set of parser test files
+ */
+ function getFuzzInput( $filenames ) {
+ $dict = '';
+
+ foreach ( $filenames as $filename ) {
+ $contents = file_get_contents( $filename );
+ preg_match_all( '/!!\s*input\n(.*?)\n!!\s*result/s', $contents, $matches );
+
+ foreach ( $matches[1] as $match ) {
+ $dict .= $match . "\n";
+ }
+ }
+
+ return $dict;
+ }
+
+ /**
+ * Get a memory usage breakdown
+ */
+ function getMemoryBreakdown() {
+ $memStats = array();
+
+ foreach ( $GLOBALS as $name => $value ) {
+ $memStats['$' . $name] = strlen( serialize( $value ) );
+ }
+
+ $classes = get_declared_classes();
+
+ foreach ( $classes as $class ) {
+ $rc = new ReflectionClass( $class );
+ $props = $rc->getStaticProperties();
+ $memStats[$class] = strlen( serialize( $props ) );
+ $methods = $rc->getMethods();
+
+ foreach ( $methods as $method ) {
+ $memStats[$class] += strlen( serialize( $method->getStaticVariables() ) );
+ }
+ }
+
+ $functions = get_defined_functions();
+
+ foreach ( $functions['user'] as $function ) {
+ $rf = new ReflectionFunction( $function );
+ $memStats["$function()"] = strlen( serialize( $rf->getStaticVariables() ) );
+ }
+
+ asort( $memStats );
+
+ return $memStats;
+ }
+
+ function abort() {
+ $this->abort();
+ }
+
+ /**
+ * Run a series of tests listed in the given text files.
+ * Each test consists of a brief description, wikitext input,
+ * and the expected HTML output.
+ *
+ * Prints status updates on stdout and counts up the total
+ * number and percentage of passed tests.
+ *
+ * @param $filenames Array of strings
+ * @return Boolean: true if passed all tests, false if any tests failed.
+ */
+ public function runTestsFromFiles( $filenames ) {
+ $ok = false;
+ $GLOBALS['wgContLang'] = Language::factory( 'en' );
+ $this->recorder->start();
+ try {
+ $this->setupDatabase();
+ $ok = true;
+
+ foreach ( $filenames as $filename ) {
+ $tests = new TestFileIterator( $filename, $this );
+ $ok = $this->runTests( $tests ) && $ok;
+ }
+
+ $this->teardownDatabase();
+ $this->recorder->report();
+ } catch (DBError $e) {
+ echo $e->getMessage();
+ }
+ $this->recorder->end();
+
+ return $ok;
+ }
+
+ function runTests( $tests ) {
+ $ok = true;
+
+ foreach ( $tests as $t ) {
+ $result =
+ $this->runTest( $t['test'], $t['input'], $t['result'], $t['options'], $t['config'] );
+ $ok = $ok && $result;
+ $this->recorder->record( $t['test'], $result );
+ }
+
+ if ( $this->showProgress ) {
+ print "\n";
+ }
+
+ return $ok;
+ }
+
+ /**
+ * Get a Parser object
+ */
+ function getParser( $preprocessor = null ) {
+ global $wgParserConf;
+
+ $class = $wgParserConf['class'];
+ $parser = new $class( array( 'preprocessorClass' => $preprocessor ) + $wgParserConf );
+
+ foreach ( $this->hooks as $tag => $callback ) {
+ $parser->setHook( $tag, $callback );
+ }
+
+ foreach ( $this->functionHooks as $tag => $bits ) {
+ list( $callback, $flags ) = $bits;
+ $parser->setFunctionHook( $tag, $callback, $flags );
+ }
+
+ wfRunHooks( 'ParserTestParser', array( &$parser ) );
+
+ return $parser;
+ }
+
+ /**
+ * Run a given wikitext input through a freshly-constructed wiki parser,
+ * and compare the output against the expected results.
+ * Prints status and explanatory messages to stdout.
+ *
+ * @param $desc String: test's description
+ * @param $input String: wikitext to try rendering
+ * @param $result String: result to output
+ * @param $opts Array: test's options
+ * @param $config String: overrides for global variables, one per line
+ * @return Boolean
+ */
+ public function runTest( $desc, $input, $result, $opts, $config ) {
+ if ( $this->showProgress ) {
+ $this->showTesting( $desc );
+ }
+
+ $opts = $this->parseOptions( $opts );
+ $this->setupGlobals( $opts, $config );
+
+ $user = new User();
+ $options = ParserOptions::newFromUser( $user );
+
+ if ( isset( $opts['title'] ) ) {
+ $titleText = $opts['title'];
+ }
+ else {
+ $titleText = 'Parser test';
+ }
+
+ $local = isset( $opts['local'] );
+ $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null;
+ $parser = $this->getParser( $preprocessor );
+ $title = Title::newFromText( $titleText );
+
+ if ( isset( $opts['pst'] ) ) {
+ $out = $parser->preSaveTransform( $input, $title, $user, $options );
+ } elseif ( isset( $opts['msg'] ) ) {
+ $out = $parser->transformMsg( $input, $options, $title );
+ } elseif ( isset( $opts['section'] ) ) {
+ $section = $opts['section'];
+ $out = $parser->getSection( $input, $section );
+ } elseif ( isset( $opts['replace'] ) ) {
+ $section = $opts['replace'][0];
+ $replace = $opts['replace'][1];
+ $out = $parser->replaceSection( $input, $section, $replace );
+ } elseif ( isset( $opts['comment'] ) ) {
+ $linker = $user->getSkin();
+ $out = $linker->formatComment( $input, $title, $local );
+ } elseif ( isset( $opts['preload'] ) ) {
+ $out = $parser->getpreloadText( $input, $title, $options );
+ } else {
+ $output = $parser->parse( $input, $title, $options, true, true, 1337 );
+ $out = $output->getText();
+
+ if ( isset( $opts['showtitle'] ) ) {
+ if ( $output->getTitleText() ) {
+ $title = $output->getTitleText();
+ }
+
+ $out = "$title\n$out";
+ }
+
+ if ( isset( $opts['ill'] ) ) {
+ $out = $this->tidy( implode( ' ', $output->getLanguageLinks() ) );
+ } elseif ( isset( $opts['cat'] ) ) {
+ global $wgOut;
+
+ $wgOut->addCategoryLinks( $output->getCategories() );
+ $cats = $wgOut->getCategoryLinks();
+
+ if ( isset( $cats['normal'] ) ) {
+ $out = $this->tidy( implode( ' ', $cats['normal'] ) );
+ } else {
+ $out = '';
+ }
+ }
+
+ $result = $this->tidy( $result );
+ }
+
+ $this->teardownGlobals();
+ return $this->showTestResult( $desc, $result, $out );
+ }
+
+ /**
+ *
+ */
+ function showTestResult( $desc, $result, $out ) {
+ if ( $result === $out ) {
+ $this->showSuccess( $desc );
+ return true;
+ } else {
+ $this->showFailure( $desc, $result, $out );
+ return false;
+ }
+ }
+
+ /**
+ * Use a regex to find out the value of an option
+ * @param $key String: name of option val to retrieve
+ * @param $opts Options array to look in
+ * @param $default Mixed: default value returned if not found
+ */
+ private static function getOptionValue( $key, $opts, $default ) {
+ $key = strtolower( $key );
+
+ if ( isset( $opts[$key] ) ) {
+ return $opts[$key];
+ } else {
+ return $default;
+ }
+ }
+
+ private function parseOptions( $instring ) {
+ $opts = array();
+ // foo
+ // foo=bar
+ // foo="bar baz"
+ // foo=[[bar baz]]
+ // foo=bar,"baz quux"
+ $regex = '/\b
+ ([\w-]+) # Key
+ \b
+ (?:\s*
+ = # First sub-value
+ \s*
+ (
+ "
+ [^"]* # Quoted val
+ "
+ |
+ \[\[
+ [^]]* # Link target
+ \]\]
+ |
+ [\w-]+ # Plain word
+ )
+ (?:\s*
+ , # Sub-vals 1..N
+ \s*
+ (
+ "[^"]*" # Quoted val
+ |
+ \[\[[^]]*\]\] # Link target
+ |
+ [\w-]+ # Plain word
+ )
+ )*
+ )?
+ /x';
+
+ if ( preg_match_all( $regex, $instring, $matches, PREG_SET_ORDER ) ) {
+ foreach ( $matches as $bits ) {
+ array_shift( $bits );
+ $key = strtolower( array_shift( $bits ) );
+ if ( count( $bits ) == 0 ) {
+ $opts[$key] = true;
+ } elseif ( count( $bits ) == 1 ) {
+ $opts[$key] = $this->cleanupOption( array_shift( $bits ) );
+ } else {
+ // Array!
+ $opts[$key] = array_map( array( $this, 'cleanupOption' ), $bits );
+ }
+ }
+ }
+ return $opts;
+ }
+
+ private function cleanupOption( $opt ) {
+ if ( substr( $opt, 0, 1 ) == '"' ) {
+ return substr( $opt, 1, -1 );
+ }
+
+ if ( substr( $opt, 0, 2 ) == '[[' ) {
+ return substr( $opt, 2, -2 );
+ }
+ return $opt;
+ }
+
+ /**
+ * Set up the global variables for a consistent environment for each test.
+ * Ideally this should replace the global configuration entirely.
+ */
+ private function setupGlobals( $opts = '', $config = '' ) {
+ # Find out values for some special options.
+ $lang =
+ self::getOptionValue( 'language', $opts, 'en' );
+ $variant =
+ self::getOptionValue( 'variant', $opts, false );
+ $maxtoclevel =
+ self::getOptionValue( 'wgMaxTocLevel', $opts, 999 );
+ $linkHolderBatchSize =
+ self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
+
+ $settings = array(
+ 'wgServer' => 'http://Britney-Spears',
+ 'wgScript' => '/index.php',
+ 'wgScriptPath' => '/',
+ 'wgArticlePath' => '/wiki/$1',
+ 'wgActionPaths' => array(),
+ 'wgLocalFileRepo' => array(
+ 'class' => 'LocalRepo',
+ 'name' => 'local',
+ 'directory' => $this->uploadDir,
+ 'url' => 'http://example.com/images',
+ 'hashLevels' => 2,
+ 'transformVia404' => false,
+ ),
+ 'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
+ 'wgStylePath' => '/skins',
+ 'wgStyleSheetPath' => '/skins',
+ 'wgSitename' => 'MediaWiki',
+ 'wgLanguageCode' => $lang,
+ 'wgDBprefix' => $this->db->getType() != 'oracle' ? 'parsertest_' : 'pt_',
+ 'wgRawHtml' => isset( $opts['rawhtml'] ),
+ 'wgLang' => null,
+ 'wgContLang' => null,
+ 'wgNamespacesWithSubpages' => array( 0 => isset( $opts['subpage'] ) ),
+ 'wgMaxTocLevel' => $maxtoclevel,
+ 'wgCapitalLinks' => true,
+ 'wgNoFollowLinks' => true,
+ 'wgNoFollowDomainExceptions' => array(),
+ 'wgThumbnailScriptPath' => false,
+ 'wgUseImageResize' => false,
+ 'wgLocaltimezone' => 'UTC',
+ 'wgAllowExternalImages' => true,
+ 'wgUseTidy' => false,
+ 'wgDefaultLanguageVariant' => $variant,
+ 'wgVariantArticlePath' => false,
+ 'wgGroupPermissions' => array( '*' => array(
+ 'createaccount' => true,
+ 'read' => true,
+ 'edit' => true,
+ 'createpage' => true,
+ 'createtalk' => true,
+ ) ),
+ 'wgNamespaceProtection' => array( NS_MEDIAWIKI => 'editinterface' ),
+ 'wgDefaultExternalStore' => array(),
+ 'wgForeignFileRepos' => array(),
+ 'wgLinkHolderBatchSize' => $linkHolderBatchSize,
+ 'wgExperimentalHtmlIds' => false,
+ 'wgExternalLinkTarget' => false,
+ 'wgAlwaysUseTidy' => false,
+ 'wgHtml5' => true,
+ 'wgWellFormedXml' => true,
+ 'wgAllowMicrodataAttributes' => true,
+ 'wgAdaptiveMessageCache' => true,
+ 'wgDisableLangConversion' => false,
+ 'wgDisableTitleConversion' => false,
+ );
+
+ if ( $config ) {
+ $configLines = explode( "\n", $config );
+
+ foreach ( $configLines as $line ) {
+ list( $var, $value ) = explode( '=', $line, 2 );
+
+ $settings[$var] = eval( "return $value;" );
+ }
+ }
+
+ $this->savedGlobals = array();
+
+ foreach ( $settings as $var => $val ) {
+ if ( array_key_exists( $var, $GLOBALS ) ) {
+ $this->savedGlobals[$var] = $GLOBALS[$var];
+ }
+
+ $GLOBALS[$var] = $val;
+ }
+
+ $GLOBALS['wgContLang'] = Language::factory( $lang );
+ $GLOBALS['wgMemc'] = new EmptyBagOStuff;
+
+ $context = new RequestContext();
+ $GLOBALS['wgLang'] = $context->getLang();
+ $GLOBALS['wgOut'] = $context->getOutput();
+
+ $GLOBALS['wgUser'] = new User();
+
+ global $wgHooks;
+
+ $wgHooks['ParserTestParser'][] = 'ParserTestParserHook::setup';
+ $wgHooks['ParserTestParser'][] = 'ParserTestStaticParserHook::setup';
+ $wgHooks['ParserGetVariableValueTs'][] = 'ParserTest::getFakeTimestamp';
+
+ MagicWord::clearCache();
+ }
+
+ /**
+ * List of temporary tables to create, without prefix.
+ * Some of these probably aren't necessary.
+ */
+ private function listTables() {
+ $tables = array( 'user', 'user_properties', 'page', 'page_restrictions',
+ 'protected_titles', 'revision', 'text', 'pagelinks', 'imagelinks',
+ 'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks',
+ 'site_stats', 'hitcounter', 'ipblocks', 'image', 'oldimage',
+ 'recentchanges', 'watchlist', 'interwiki', 'logging',
+ 'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo',
+ 'archive', 'user_groups', 'page_props', 'category', 'msg_resource', 'msg_resource_links'
+ );
+
+ if ( in_array( $this->db->getType(), array( 'mysql', 'sqlite', 'oracle' ) ) )
+ array_push( $tables, 'searchindex' );
+
+ // Allow extensions to add to the list of tables to duplicate;
+ // may be necessary if they hook into page save or other code
+ // which will require them while running tests.
+ wfRunHooks( 'ParserTestTables', array( &$tables ) );
+
+ return $tables;
+ }
+
+ /**
+ * Set up a temporary set of wiki tables to work with for the tests.
+ * Currently this will only be done once per run, and any changes to
+ * the db will be visible to later tests in the run.
+ */
+ public function setupDatabase() {
+ global $wgDBprefix;
+
+ if ( $this->databaseSetupDone ) {
+ return;
+ }
+
+ $this->db = wfGetDB( DB_MASTER );
+ $dbType = $this->db->getType();
+
+ if ( $wgDBprefix === 'parsertest_' || ( $dbType == 'oracle' && $wgDBprefix === 'pt_' ) ) {
+ throw new MWException( 'setupDatabase should be called before setupGlobals' );
+ }
+
+ $this->databaseSetupDone = true;
+ $this->oldTablePrefix = $wgDBprefix;
+
+ # SqlBagOStuff broke when using temporary tables on r40209 (bug 15892).
+ # It seems to have been fixed since (r55079?), but regressed at some point before r85701.
+ # This works around it for now...
+ ObjectCache::$instances[CACHE_DB] = new HashBagOStuff;
+
+ # CREATE TEMPORARY TABLE breaks if there is more than one server
+ if ( wfGetLB()->getServerCount() != 1 ) {
+ $this->useTemporaryTables = false;
+ }
+
+ $temporary = $this->useTemporaryTables || $dbType == 'postgres';
+ $tables = $this->listTables();
+ $prefix = $dbType != 'oracle' ? 'parsertest_' : 'pt_';
+
+ $this->dbClone = new CloneDatabase( $this->db, $this->listTables(), $prefix );
+ $this->dbClone->useTemporaryTables( $temporary );
+ $this->dbClone->cloneTableStructure();
+
+ if ( $dbType == 'oracle' )
+ $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
+
+ if ( $dbType == 'oracle' ) {
+ # Insert 0 user to prevent FK violations
+
+ # Anonymous user
+ $this->db->insert( 'user', array(
+ 'user_id' => 0,
+ 'user_name' => 'Anonymous' ) );
+ }
+
+ # Hack: insert a few Wikipedia in-project interwiki prefixes,
+ # for testing inter-language links
+ $this->db->insert( 'interwiki', array(
+ array( 'iw_prefix' => 'wikipedia',
+ 'iw_url' => 'http://en.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 0 ),
+ array( 'iw_prefix' => 'meatball',
+ 'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 0 ),
+ array( 'iw_prefix' => 'zh',
+ 'iw_url' => 'http://zh.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 1 ),
+ array( 'iw_prefix' => 'es',
+ 'iw_url' => 'http://es.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 1 ),
+ array( 'iw_prefix' => 'fr',
+ 'iw_url' => 'http://fr.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 1 ),
+ array( 'iw_prefix' => 'ru',
+ 'iw_url' => 'http://ru.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 1 ),
+ ) );
+
+
+ # Update certain things in site_stats
+ $this->db->insert( 'site_stats', array( 'ss_row_id' => 1, 'ss_images' => 2, 'ss_good_articles' => 1 ) );
+
+ # Reinitialise the LocalisationCache to match the database state
+ Language::getLocalisationCache()->unloadAll();
+
+ # Clear the message cache
+ MessageCache::singleton()->clear();
+
+ $this->uploadDir = $this->setupUploadDir();
+ $user = User::createNew( 'WikiSysop' );
+ $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
+ $image->recordUpload2( '', 'Upload of some lame file', 'Some lame file', array(
+ 'size' => 12345,
+ 'width' => 1941,
+ 'height' => 220,
+ 'bits' => 24,
+ 'media_type' => MEDIATYPE_BITMAP,
+ 'mime' => 'image/jpeg',
+ 'metadata' => serialize( array() ),
+ 'sha1' => wfBaseConvert( '', 16, 36, 31 ),
+ 'fileExists' => true
+ ), $this->db->timestamp( '20010115123500' ), $user );
+
+ # This image will be blacklisted in [[MediaWiki:Bad image list]]
+ $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
+ $image->recordUpload2( '', 'zomgnotcensored', 'Borderline image', array(
+ 'size' => 12345,
+ 'width' => 320,
+ 'height' => 240,
+ 'bits' => 24,
+ 'media_type' => MEDIATYPE_BITMAP,
+ 'mime' => 'image/jpeg',
+ 'metadata' => serialize( array() ),
+ 'sha1' => wfBaseConvert( '', 16, 36, 31 ),
+ 'fileExists' => true
+ ), $this->db->timestamp( '20010115123500' ), $user );
+ }
+
+ public function teardownDatabase() {
+ if ( !$this->databaseSetupDone ) {
+ $this->teardownGlobals();
+ return;
+ }
+ $this->teardownUploadDir( $this->uploadDir );
+
+ $this->dbClone->destroy();
+ $this->databaseSetupDone = false;
+
+ if ( $this->useTemporaryTables ) {
+ if( $this->db->getType() == 'sqlite' ) {
+ # Under SQLite the searchindex table is virtual and need
+ # to be explicitly destroyed. See bug 29912
+ # See also MediaWikiTestCase::destroyDB()
+ wfDebug( __METHOD__ . " explicitly destroying sqlite virtual table parsertest_searchindex\n" );
+ $this->db->query( "DROP TABLE `parsertest_searchindex`" );
+ }
+ # Don't need to do anything
+ $this->teardownGlobals();
+ return;
+ }
+
+ $tables = $this->listTables();
+
+ foreach ( $tables as $table ) {
+ $sql = $this->db->getType() == 'oracle' ? "DROP TABLE pt_$table DROP CONSTRAINTS" : "DROP TABLE `parsertest_$table`";
+ $this->db->query( $sql );
+ }
+
+ if ( $this->db->getType() == 'oracle' )
+ $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
+
+ $this->teardownGlobals();
+ }
+
+ /**
+ * Create a dummy uploads directory which will contain a couple
+ * of files in order to pass existence tests.
+ *
+ * @return String: the directory
+ */
+ private function setupUploadDir() {
+ global $IP;
+
+ if ( $this->keepUploads ) {
+ $dir = wfTempDir() . '/mwParser-images';
+
+ if ( is_dir( $dir ) ) {
+ return $dir;
+ }
+ } else {
+ $dir = wfTempDir() . "/mwParser-" . mt_rand() . "-images";
+ }
+
+ // wfDebug( "Creating upload directory $dir\n" );
+ if ( file_exists( $dir ) ) {
+ wfDebug( "Already exists!\n" );
+ return $dir;
+ }
+
+ wfMkdirParents( $dir . '/3/3a' );
+ copy( "$IP/skins/monobook/headbg.jpg", "$dir/3/3a/Foobar.jpg" );
+ wfMkdirParents( $dir . '/0/09' );
+ copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" );
+
+ return $dir;
+ }
+
+ /**
+ * Restore default values and perform any necessary clean-up
+ * after each test runs.
+ */
+ private function teardownGlobals() {
+ RepoGroup::destroySingleton();
+ LinkCache::singleton()->clear();
+
+ foreach ( $this->savedGlobals as $var => $val ) {
+ $GLOBALS[$var] = $val;
+ }
+ }
+
+ /**
+ * Remove the dummy uploads directory
+ */
+ private function teardownUploadDir( $dir ) {
+ if ( $this->keepUploads ) {
+ return;
+ }
+
+ // delete the files first, then the dirs.
+ self::deleteFiles(
+ array (
+ "$dir/3/3a/Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/200px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/640px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg",
+
+ "$dir/0/09/Bad.jpg",
+
+ "$dir/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
+ )
+ );
+
+ self::deleteDirs(
+ array (
+ "$dir/3/3a",
+ "$dir/3",
+ "$dir/thumb/6/65",
+ "$dir/thumb/6",
+ "$dir/thumb/3/3a/Foobar.jpg",
+ "$dir/thumb/3/3a",
+ "$dir/thumb/3",
+
+ "$dir/0/09/",
+ "$dir/0/",
+ "$dir/thumb",
+ "$dir/math/f/a/5",
+ "$dir/math/f/a",
+ "$dir/math/f",
+ "$dir/math",
+ "$dir",
+ )
+ );
+ }
+
+ /**
+ * Delete the specified files, if they exist.
+ * @param $files Array: full paths to files to delete.
+ */
+ private static function deleteFiles( $files ) {
+ foreach ( $files as $file ) {
+ if ( file_exists( $file ) ) {
+ unlink( $file );
+ }
+ }
+ }
+
+ /**
+ * Delete the specified directories, if they exist. Must be empty.
+ * @param $dirs Array: full paths to directories to delete.
+ */
+ private static function deleteDirs( $dirs ) {
+ foreach ( $dirs as $dir ) {
+ if ( is_dir( $dir ) ) {
+ rmdir( $dir );
+ }
+ }
+ }
+
+ /**
+ * "Running test $desc..."
+ */
+ protected function showTesting( $desc ) {
+ print "Running test $desc... ";
+ }
+
+ /**
+ * Print a happy success message.
+ *
+ * @param $desc String: the test name
+ * @return Boolean
+ */
+ protected function showSuccess( $desc ) {
+ if ( $this->showProgress ) {
+ print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n";
+ }
+
+ return true;
+ }
+
+ /**
+ * Print a failure message and provide some explanatory output
+ * about what went wrong if so configured.
+ *
+ * @param $desc String: the test name
+ * @param $result String: expected HTML output
+ * @param $html String: actual HTML output
+ * @return Boolean
+ */
+ protected function showFailure( $desc, $result, $html ) {
+ if ( $this->showFailure ) {
+ if ( !$this->showProgress ) {
+ # In quiet mode we didn't show the 'Testing' message before the
+ # test, in case it succeeded. Show it now:
+ $this->showTesting( $desc );
+ }
+
+ print $this->term->color( '31' ) . 'FAILED!' . $this->term->reset() . "\n";
+
+ if ( $this->showOutput ) {
+ print "--- Expected ---\n$result\n--- Actual ---\n$html\n";
+ }
+
+ if ( $this->showDiffs ) {
+ print $this->quickDiff( $result, $html );
+ if ( !$this->wellFormed( $html ) ) {
+ print "XML error: $this->mXmlError\n";
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Run given strings through a diff and return the (colorized) output.
+ * Requires writable /tmp directory and a 'diff' command in the PATH.
+ *
+ * @param $input String
+ * @param $output String
+ * @param $inFileTail String: tailing for the input file name
+ * @param $outFileTail String: tailing for the output file name
+ * @return String
+ */
+ protected function quickDiff( $input, $output, $inFileTail = 'expected', $outFileTail = 'actual' ) {
+ # Windows, or at least the fc utility, is retarded
+ $slash = wfIsWindows() ? '\\' : '/';
+ $prefix = wfTempDir() . "{$slash}mwParser-" . mt_rand();
+
+ $infile = "$prefix-$inFileTail";
+ $this->dumpToFile( $input, $infile );
+
+ $outfile = "$prefix-$outFileTail";
+ $this->dumpToFile( $output, $outfile );
+
+ $shellInfile = wfEscapeShellArg($infile);
+ $shellOutfile = wfEscapeShellArg($outfile);
+
+ $diff = wfIsWindows()
+ ? `fc $shellInfile $shellOutfile`
+ : `diff -au $shellInfile $shellOutfile`;
+ unlink( $infile );
+ unlink( $outfile );
+
+ return $this->colorDiff( $diff );
+ }
+
+ /**
+ * Write the given string to a file, adding a final newline.
+ *
+ * @param $data String
+ * @param $filename String
+ */
+ private function dumpToFile( $data, $filename ) {
+ $file = fopen( $filename, "wt" );
+ fwrite( $file, $data . "\n" );
+ fclose( $file );
+ }
+
+ /**
+ * Colorize unified diff output if set for ANSI color output.
+ * Subtractions are colored blue, additions red.
+ *
+ * @param $text String
+ * @return String
+ */
+ protected function colorDiff( $text ) {
+ return preg_replace(
+ array( '/^(-.*)$/m', '/^(\+.*)$/m' ),
+ array( $this->term->color( 34 ) . '$1' . $this->term->reset(),
+ $this->term->color( 31 ) . '$1' . $this->term->reset() ),
+ $text );
+ }
+
+ /**
+ * Show "Reading tests from ..."
+ *
+ * @param $path String
+ */
+ public function showRunFile( $path ) {
+ print $this->term->color( 1 ) .
+ "Reading tests from \"$path\"..." .
+ $this->term->reset() .
+ "\n";
+ }
+
+ /**
+ * Insert a temporary test article
+ * @param $name String: the title, including any prefix
+ * @param $text String: the article text
+ * @param $line Integer: the input line number, for reporting errors
+ */
+ static public function addArticle( $name, $text, $line = 'unknown' ) {
+ global $wgCapitalLinks;
+
+ $text = self::chomp($text);
+
+ $oldCapitalLinks = $wgCapitalLinks;
+ $wgCapitalLinks = true; // We only need this from SetupGlobals() See r70917#c8637
+
+ $name = self::chomp( $name );
+ $title = Title::newFromText( $name );
+
+ if ( is_null( $title ) ) {
+ throw new MWException( "invalid title ('$name' => '$title') at line $line\n" );
+ }
+
+ $aid = $title->getArticleID( Title::GAID_FOR_UPDATE );
+
+ if ( $aid != 0 ) {
+ throw new MWException( "duplicate article '$name' at line $line\n" );
+ }
+
+ $art = new Article( $title );
+ $art->doEdit( $text, '', EDIT_NEW );
+
+ $wgCapitalLinks = $oldCapitalLinks;
+ }
+
+ /**
+ * Steal a callback function from the primary parser, save it for
+ * application to our scary parser. If the hook is not installed,
+ * abort processing of this file.
+ *
+ * @param $name String
+ * @return Bool true if tag hook is present
+ */
+ public function requireHook( $name ) {
+ global $wgParser;
+
+ $wgParser->firstCallInit( ); // make sure hooks are loaded.
+
+ if ( isset( $wgParser->mTagHooks[$name] ) ) {
+ $this->hooks[$name] = $wgParser->mTagHooks[$name];
+ } else {
+ echo " This test suite requires the '$name' hook extension, skipping.\n";
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Steal a callback function from the primary parser, save it for
+ * application to our scary parser. If the hook is not installed,
+ * abort processing of this file.
+ *
+ * @param $name String
+ * @return Bool true if function hook is present
+ */
+ public function requireFunctionHook( $name ) {
+ global $wgParser;
+
+ $wgParser->firstCallInit( ); // make sure hooks are loaded.
+
+ if ( isset( $wgParser->mFunctionHooks[$name] ) ) {
+ $this->functionHooks[$name] = $wgParser->mFunctionHooks[$name];
+ } else {
+ echo " This test suite requires the '$name' function hook extension, skipping.\n";
+ return false;
+ }
+
+ return true;
+ }
+
+ /*
+ * Run the "tidy" command on text if the $wgUseTidy
+ * global is true
+ *
+ * @param $text String: the text to tidy
+ * @return String
+ */
+ private function tidy( $text ) {
+ global $wgUseTidy;
+
+ if ( $wgUseTidy ) {
+ $text = MWTidy::tidy( $text );
+ }
+
+ return $text;
+ }
+
+ private function wellFormed( $text ) {
+ $html =
+ Sanitizer::hackDocType() .
+ '<html>' .
+ $text .
+ '</html>';
+
+ $parser = xml_parser_create( "UTF-8" );
+
+ # case folding violates XML standard, turn it off
+ xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
+
+ if ( !xml_parse( $parser, $html, true ) ) {
+ $err = xml_error_string( xml_get_error_code( $parser ) );
+ $position = xml_get_current_byte_index( $parser );
+ $fragment = $this->extractFragment( $html, $position );
+ $this->mXmlError = "$err at byte $position:\n$fragment";
+ xml_parser_free( $parser );
+
+ return false;
+ }
+
+ xml_parser_free( $parser );
+
+ return true;
+ }
+
+ private function extractFragment( $text, $position ) {
+ $start = max( 0, $position - 10 );
+ $before = $position - $start;
+ $fragment = '...' .
+ $this->term->color( 34 ) .
+ substr( $text, $start, $before ) .
+ $this->term->color( 0 ) .
+ $this->term->color( 31 ) .
+ $this->term->color( 1 ) .
+ substr( $text, $position, 1 ) .
+ $this->term->color( 0 ) .
+ $this->term->color( 34 ) .
+ substr( $text, $position + 1, 9 ) .
+ $this->term->color( 0 ) .
+ '...';
+ $display = str_replace( "\n", ' ', $fragment );
+ $caret = ' ' .
+ str_repeat( ' ', $before ) .
+ $this->term->color( 31 ) .
+ '^' .
+ $this->term->color( 0 );
+
+ return "$display\n$caret";
+ }
+
+ static function getFakeTimestamp( &$parser, &$ts ) {
+ $ts = 123;
+ return true;
+ }
+}
diff --git a/tests/parser/parserTests.txt b/tests/parser/parserTests.txt
new file mode 100644
index 00000000..6ea832e3
--- /dev/null
+++ b/tests/parser/parserTests.txt
@@ -0,0 +1,8824 @@
+# MediaWiki Parser test cases
+# Some taken from http://meta.wikimedia.org/wiki/Parser_testing
+# All (C) their respective authors and released under the GPL
+#
+# The syntax should be fairly self-explanatory.
+#
+# Currently supported test options:
+# One of the following three:
+#
+# (default) generate HTML output
+# pst apply pre-save transform
+# msg apply message transform
+#
+# Plus any combination of these:
+#
+# cat add category links
+# ill add inter-language links
+# subpage enable subpages (disabled by default)
+# noxml don't check for XML well formdness
+# title=[[XXX]] run test using article title XXX
+# language=XXX set content language to XXX for this test
+# variant=XXX set the variant of language for this test (eg zh-tw)
+# disabled do not run test
+# showtitle make the first line the title
+# comment run through Linker::formatComment() instead of main parser
+# local format section links in edit comment text as local links
+#
+# For testing purposes, temporary articles can created:
+# !!article / NAMESPACE:TITLE / !!text / ARTICLE TEXT / !!endarticle
+# where '/' denotes a newline.
+
+# This is the standard article assumed to exist.
+!! article
+Main Page
+!! text
+blah blah
+!! endarticle
+
+!!article
+Template:Foo
+!!text
+FOO
+!!endarticle
+
+!! article
+Template:Blank
+!! text
+!! endarticle
+
+!! article
+Template:!
+!! text
+|
+!! endarticle
+
+!!article
+MediaWiki:bad image list
+!!text
+* [[File:Bad.jpg]] except [[Nasty page]]
+!!endarticle
+
+###
+### Basic tests
+###
+!! test
+Blank input
+!! input
+!! result
+!! end
+
+
+!! test
+Simple paragraph
+!! input
+This is a simple paragraph.
+!! result
+<p>This is a simple paragraph.
+</p>
+!! end
+
+!! test
+Simple list
+!! input
+* Item 1
+* Item 2
+!! result
+<ul><li> Item 1
+</li><li> Item 2
+</li></ul>
+
+!! end
+
+!! test
+Italics and bold
+!! input
+* plain
+* plain''italic''plain
+* plain''italic''plain''italic''plain
+* plain'''bold'''plain
+* plain'''bold'''plain'''bold'''plain
+* plain''italic''plain'''bold'''plain
+* plain'''bold'''plain''italic''plain
+* plain''italic'''bold-italic'''italic''plain
+* plain'''bold''bold-italic''bold'''plain
+* plain'''''bold-italic'''italic''plain
+* plain'''''bold-italic''bold'''plain
+* plain''italic'''bold-italic'''''plain
+* plain'''bold''bold-italic'''''plain
+* plain l'''italic''plain
+* plain l''''bold''' plain
+!! result
+<ul><li> plain
+</li><li> plain<i>italic</i>plain
+</li><li> plain<i>italic</i>plain<i>italic</i>plain
+</li><li> plain<b>bold</b>plain
+</li><li> plain<b>bold</b>plain<b>bold</b>plain
+</li><li> plain<i>italic</i>plain<b>bold</b>plain
+</li><li> plain<b>bold</b>plain<i>italic</i>plain
+</li><li> plain<i>italic<b>bold-italic</b>italic</i>plain
+</li><li> plain<b>bold<i>bold-italic</i>bold</b>plain
+</li><li> plain<i><b>bold-italic</b>italic</i>plain
+</li><li> plain<b><i>bold-italic</i>bold</b>plain
+</li><li> plain<i>italic<b>bold-italic</b></i>plain
+</li><li> plain<b>bold<i>bold-italic</i></b>plain
+</li><li> plain l'<i>italic</i>plain
+</li><li> plain l'<b>bold</b> plain
+</li></ul>
+
+!! end
+
+###
+### <nowiki> test cases
+###
+
+!! test
+<nowiki> unordered list
+!! input
+<nowiki>* This is not an unordered list item.</nowiki>
+!! result
+<p>* This is not an unordered list item.
+</p>
+!! end
+
+!! test
+<nowiki> spacing
+!! input
+<nowiki>Lorem ipsum dolor
+
+sed abit.
+ sed nullum.
+
+:and a colon
+</nowiki>
+!! result
+<p>Lorem ipsum dolor
+
+sed abit.
+ sed nullum.
+
+:and a colon
+
+</p>
+!! end
+
+!! test
+nowiki 3
+!! input
+:There is not nowiki.
+:There is <nowiki>nowiki</nowiki>.
+
+#There is not nowiki.
+#There is <nowiki>nowiki</nowiki>.
+
+*There is not nowiki.
+*There is <nowiki>nowiki</nowiki>.
+!! result
+<dl><dd>There is not nowiki.
+</dd><dd>There is nowiki.
+</dd></dl>
+<ol><li>There is not nowiki.
+</li><li>There is nowiki.
+</li></ol>
+<ul><li>There is not nowiki.
+</li><li>There is nowiki.
+</li></ul>
+
+!! end
+
+
+###
+### Comments
+###
+!! test
+Comment test 1
+!! input
+<!-- comment 1 --> asdf
+<!-- comment 2 -->
+!! result
+<pre>asdf
+</pre>
+
+!! end
+
+!! test
+Comment test 2
+!! input
+asdf
+<!-- comment 1 -->
+jkl
+!! result
+<p>asdf
+jkl
+</p>
+!! end
+
+!! test
+Comment test 3
+!! input
+asdf
+<!-- comment 1 -->
+<!-- comment 2 -->
+jkl
+!! result
+<p>asdf
+jkl
+</p>
+!! end
+
+!! test
+Comment test 4
+!! input
+asdf<!-- comment 1 -->jkl
+!! result
+<p>asdfjkl
+</p>
+!! end
+
+!! test
+Comment spacing
+!! input
+a
+ <!-- foo --> b <!-- bar -->
+c
+!! result
+<p>a
+</p>
+<pre> b
+</pre>
+<p>c
+</p>
+!! end
+
+!! test
+Comment whitespace
+!! input
+<!-- returns a single newline, not nothing, since the newline after > is not stripped -->
+!! result
+
+!! end
+
+!! test
+Comment semantics and delimiters
+!! input
+<!-- --><!----><!-----><!------>
+!! result
+
+!! end
+
+!! test
+Comment semantics and delimiters, redux
+!! input
+<!-- In SGML every "foo" here would actually show up in the text -- foo -- bar
+-- foo -- funky huh? ... -->
+!! result
+
+!! end
+
+!! test
+Comment semantics and delimiters: directors cut
+!! input
+<!-- ... However we like to keep things simple and somewhat XML-ish so we eat
+everything starting with < followed by !-- until the first -- and > we see,
+that wouldn't be valid XML however, since in XML -- has to terminate a comment
+-->-->
+!! result
+<p>--&gt;
+</p>
+!! end
+
+!! test
+Comment semantics: nesting
+!! input
+<!--<!-- no, we're not going to do anything fancy here -->-->
+!! result
+<p>--&gt;
+</p>
+!! end
+
+!! test
+Comment semantics: unclosed comment at end
+!! input
+<!--This comment will run out to the end of the document
+!! result
+
+!! end
+
+!! test
+Comment in template title
+!! input
+{{f<!---->oo}}
+!! result
+<p>FOO
+</p>
+!! end
+
+!! test
+Comment on its own line post-expand
+!! input
+a
+{{blank}}<!---->
+b
+!! result
+<p>a
+</p><p>b
+</p>
+!! end
+
+###
+### Preformatted text
+###
+!! test
+Preformatted text
+!! input
+ This is some
+ Preformatted text
+ With ''italic''
+ And '''bold'''
+ And a [[Main Page|link]]
+!! result
+<pre>This is some
+Preformatted text
+With <i>italic</i>
+And <b>bold</b>
+And a <a href="/wiki/Main_Page" title="Main Page">link</a>
+</pre>
+!! end
+
+!! test
+<pre> with <nowiki> inside (compatibility with 1.6 and earlier)
+!! input
+<pre><nowiki>
+<b>
+<cite>
+<em>
+</nowiki></pre>
+!! result
+<pre>
+&lt;b&gt;
+&lt;cite&gt;
+&lt;em&gt;
+</pre>
+
+!! end
+
+!! test
+Regression with preformatted in <center>
+!! input
+<center>
+ Blah
+</center>
+!! result
+<center>
+<pre>Blah
+</pre>
+</center>
+
+!! end
+
+# Expected output in the following test is not really expected (there should be
+# <pre> in the output) -- it's only testing for well-formedness.
+!! test
+Bug 6200: Preformatted in <blockquote>
+!! input
+<blockquote>
+ Blah
+</blockquote>
+!! result
+<blockquote>
+ Blah
+</blockquote>
+
+!! end
+
+!! test
+<pre> with attributes (bug 3202)
+!! input
+<pre style="background: blue; color:white">Bluescreen of WikiDeath</pre>
+!! result
+<pre style="background: blue; color:white">Bluescreen of WikiDeath</pre>
+
+!! end
+
+!! test
+<pre> with width attribute (bug 3202)
+!! input
+<pre width="8">Narrow screen goodies</pre>
+!! result
+<pre width="8">Narrow screen goodies</pre>
+
+!! end
+
+!! test
+<pre> with forbidden attribute (bug 3202)
+!! input
+<pre width="8" onmouseover="alert(document.cookie)">Narrow screen goodies</pre>
+!! result
+<pre width="8">Narrow screen goodies</pre>
+
+!! end
+
+!! test
+<pre> with forbidden attribute values (bug 3202)
+!! input
+<pre width="8" style="border-width: expression(alert(document.cookie))">Narrow screen goodies</pre>
+!! result
+<pre width="8" style="/* insecure input */">Narrow screen goodies</pre>
+
+!! end
+
+!! test
+<nowiki> inside <pre> (bug 13238)
+!! input
+<pre>
+<nowiki>
+</pre>
+<pre>
+<nowiki></nowiki>
+</pre>
+<pre><nowiki><nowiki></nowiki>Foo<nowiki></nowiki></nowiki></pre>
+!! result
+<pre>
+&lt;nowiki&gt;
+</pre>
+<pre>
+
+</pre>
+<pre>&lt;nowiki&gt;Foo&lt;/nowiki&gt;</pre>
+
+!! end
+
+!! test
+<nowiki> and <pre> preference (first one wins)
+!! input
+<pre>
+<nowiki>
+</pre>
+</nowiki>
+</pre>
+
+<nowiki>
+<pre>
+<nowiki>
+</pre>
+</nowiki>
+</pre>
+
+!! result
+<pre>
+&lt;nowiki&gt;
+</pre>
+<p>&lt;/nowiki&gt;
+&lt;/pre&gt;
+</p><p>
+&lt;pre&gt;
+&lt;nowiki&gt;
+&lt;/pre&gt;
+
+&lt;/pre&gt;
+</p>
+!! end
+
+
+###
+### Definition lists
+###
+!! test
+Simple definition
+!! input
+; name : Definition
+!! result
+<dl><dt> name&#160;</dt><dd> Definition
+</dd></dl>
+
+!! end
+
+!! test
+Definition list for indentation only
+!! input
+: Indented text
+!! result
+<dl><dd> Indented text
+</dd></dl>
+
+!! end
+
+!! test
+Definition list with no space
+!! input
+;name:Definition
+!! result
+<dl><dt>name</dt><dd>Definition
+</dd></dl>
+
+!!end
+
+!! test
+Definition list with URL link
+!! input
+; http://example.com/ : definition
+!! result
+<dl><dt> <a rel="nofollow" class="external free" href="http://example.com/">http://example.com/</a>&#160;</dt><dd> definition
+</dd></dl>
+
+!! end
+
+!! test
+Definition list with bracketed URL link
+!! input
+;[http://www.example.com/ Example]:Something about it
+!! result
+<dl><dt><a rel="nofollow" class="external text" href="http://www.example.com/">Example</a></dt><dd>Something about it
+</dd></dl>
+
+!! end
+
+!! test
+Definition list with wikilink containing colon
+!! input
+; [[Help:FAQ]]: The least-read page on Wikipedia
+!! result
+<dl><dt> <a href="/index.php?title=Help:FAQ&amp;action=edit&amp;redlink=1" class="new" title="Help:FAQ (page does not exist)">Help:FAQ</a></dt><dd> The least-read page on Wikipedia
+</dd></dl>
+
+!! end
+
+# At Brion's and JeLuF's insistence... :)
+!! test
+Definition list with news link containing colon
+!! input
+; news:alt.wikipedia.rox: This isn't even a real newsgroup!
+!! result
+<dl><dt> <a rel="nofollow" class="external free" href="news:alt.wikipedia.rox">news:alt.wikipedia.rox</a></dt><dd> This isn't even a real newsgroup!
+</dd></dl>
+
+!! end
+
+!! test
+Malformed definition list with colon
+!! input
+; news:alt.wikipedia.rox -- don't crash or enter an infinite loop
+!! result
+<dl><dt> <a rel="nofollow" class="external free" href="news:alt.wikipedia.rox">news:alt.wikipedia.rox</a> -- don't crash or enter an infinite loop
+</dt></dl>
+
+!! end
+
+!! test
+Definition lists: colon in external link text
+!! input
+; [http://www.wikipedia2.org/ Wikipedia : The Next Generation]: OK, I made that up
+!! result
+<dl><dt> <a rel="nofollow" class="external text" href="http://www.wikipedia2.org/">Wikipedia&#160;: The Next Generation</a></dt><dd> OK, I made that up
+</dd></dl>
+
+!! end
+
+!! test
+Definition lists: colon in HTML attribute
+!! input
+;<b style="display: inline">bold</b>
+!! result
+<dl><dt><b style="display: inline">bold</b>
+</dt></dl>
+
+!! end
+
+
+!! test
+Definition lists: self-closed tag
+!! input
+;one<br/>two : two-line fun
+!! result
+<dl><dt>one<br />two&#160;</dt><dd> two-line fun
+</dd></dl>
+
+!! end
+
+!! test
+Bug 11748: Literal closing tags
+!! options
+disabled
+!! input
+<dl>
+<dt>test 1</dt>
+<dd>test test test test test</dd>
+<dt>test 2</dt>
+<dd>test test test test test</dd>
+</dl>
+!! result
+<dl>
+<dt>test 1</dt>
+<dd>test test test test test</dd>
+<dt>test 2</dt>
+<dd>test test test test test</dd>
+</dl>
+!! end
+
+###
+### External links
+###
+!! test
+External links: non-bracketed
+!! input
+Non-bracketed: http://example.com
+!! result
+<p>Non-bracketed: <a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>
+</p>
+!! end
+
+!! test
+External links: numbered
+!! input
+Numbered: [http://example.com]
+Numbered: [http://example.net]
+Numbered: [http://example.com]
+!! result
+<p>Numbered: <a rel="nofollow" class="external autonumber" href="http://example.com">[1]</a>
+Numbered: <a rel="nofollow" class="external autonumber" href="http://example.net">[2]</a>
+Numbered: <a rel="nofollow" class="external autonumber" href="http://example.com">[3]</a>
+</p>
+!!end
+
+!! test
+External links: specified text
+!! input
+Specified text: [http://example.com link]
+!! result
+<p>Specified text: <a rel="nofollow" class="external text" href="http://example.com">link</a>
+</p>
+!!end
+
+!! test
+External links: trail
+!! input
+Linktrails should not work for external links: [http://example.com link]s
+!! result
+<p>Linktrails should not work for external links: <a rel="nofollow" class="external text" href="http://example.com">link</a>s
+</p>
+!! end
+
+!! test
+External links: dollar sign in URL
+!! input
+http://example.com/1$2345
+!! result
+<p><a rel="nofollow" class="external free" href="http://example.com/1$2345">http://example.com/1$2345</a>
+</p>
+!! end
+
+!! test
+External links: dollar sign in URL (named)
+!! input
+[http://example.com/1$2345]
+!! result
+<p><a rel="nofollow" class="external autonumber" href="http://example.com/1$2345">[1]</a>
+</p>
+!!end
+
+!! test
+External links: open square bracket forbidden in URL (bug 4377)
+!! input
+http://example.com/1[2345
+!! result
+<p><a rel="nofollow" class="external free" href="http://example.com/1">http://example.com/1</a>[2345
+</p>
+!! end
+
+!! test
+External links: open square bracket forbidden in URL (named) (bug 4377)
+!! input
+[http://example.com/1[2345]
+!! result
+<p><a rel="nofollow" class="external text" href="http://example.com/1">[2345</a>
+</p>
+!!end
+
+!! test
+External links: nowiki in URL link text (bug 6230)
+!!input
+[http://example.com/ <nowiki>''example site''</nowiki>]
+!! result
+<p><a rel="nofollow" class="external text" href="http://example.com/">''example site''</a>
+</p>
+!! end
+
+!! test
+External links: newline forbidden in text (bug 6230 regression check)
+!! input
+[http://example.com/ first
+second]
+!! result
+<p>[<a rel="nofollow" class="external free" href="http://example.com/">http://example.com/</a> first
+second]
+</p>
+!!end
+
+!! test
+External links: protocol-relative URL in brackets
+!! input
+[//example.com/ Test]
+!! result
+<p><a rel="nofollow" class="external text" href="//example.com/">Test</a>
+</p>
+!! end
+
+!! test
+External links: protocol-relative URL in brackets without text
+!! input
+[//example.com]
+!! result
+<p><a rel="nofollow" class="external autonumber" href="//example.com">[1]</a>
+</p>
+!! end
+
+!! test
+External links: protocol-relative URL in free text is left alone
+!! input
+//example.com/Foo
+!! result
+<p>//example.com/Foo
+</p>
+!!end
+
+!! test
+External links: protocol-relative URL in the middle of a word is left alone (bug 30269)
+!! input
+foo//example.com/Foo
+!! result
+<p>foo//example.com/Foo
+</p>
+!! end
+
+!! test
+External image
+!! input
+External image: http://meta.wikimedia.org/upload/f/f1/Ncwikicol.png
+!! result
+<p>External image: <img src="http://meta.wikimedia.org/upload/f/f1/Ncwikicol.png" alt="Ncwikicol.png" />
+</p>
+!! end
+
+!! test
+External image from https
+!! input
+External image from https: https://meta.wikimedia.org/upload/f/f1/Ncwikicol.png
+!! result
+<p>External image from https: <img src="https://meta.wikimedia.org/upload/f/f1/Ncwikicol.png" alt="Ncwikicol.png" />
+</p>
+!! end
+
+!! test
+Link to non-http image, no img tag
+!! input
+Link to non-http image, no img tag: ftp://example.com/test.jpg
+!! result
+<p>Link to non-http image, no img tag: <a rel="nofollow" class="external free" href="ftp://example.com/test.jpg">ftp://example.com/test.jpg</a>
+</p>
+!! end
+
+!! test
+External links: terminating separator
+!! input
+Terminating separator: http://example.com/thing,
+!! result
+<p>Terminating separator: <a rel="nofollow" class="external free" href="http://example.com/thing">http://example.com/thing</a>,
+</p>
+!! end
+
+!! test
+External links: intervening separator
+!! input
+Intervening separator: http://example.com/1,2,3
+!! result
+<p>Intervening separator: <a rel="nofollow" class="external free" href="http://example.com/1,2,3">http://example.com/1,2,3</a>
+</p>
+!! end
+
+!! test
+External links: old bug with URL in query
+!! input
+Old bug with URL in query: [http://example.com/thing?url=http://example.com link]
+!! result
+<p>Old bug with URL in query: <a rel="nofollow" class="external text" href="http://example.com/thing?url=http://example.com">link</a>
+</p>
+!! end
+
+!! test
+External links: old URL-in-URL bug, mixed protocols
+!! input
+And again with mixed protocols: [ftp://example.com?url=http://example.com link]
+!! result
+<p>And again with mixed protocols: <a rel="nofollow" class="external text" href="ftp://example.com?url=http://example.com">link</a>
+</p>
+!!end
+
+!! test
+External links: URL in text
+!! input
+URL in text: [http://example.com http://example.com]
+!! result
+<p>URL in text: <a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>
+</p>
+!! end
+
+!! test
+External links: Clickable images
+!! input
+ja-style clickable images: [http://example.com http://meta.wikimedia.org/upload/f/f1/Ncwikicol.png]
+!! result
+<p>ja-style clickable images: <a rel="nofollow" class="external text" href="http://example.com"><img src="http://meta.wikimedia.org/upload/f/f1/Ncwikicol.png" alt="Ncwikicol.png" /></a>
+</p>
+!!end
+
+!! test
+External links: raw ampersand
+!! input
+Old &amp; use: http://x&y
+!! result
+<p>Old &amp; use: <a rel="nofollow" class="external free" href="http://x&amp;y">http://x&amp;y</a>
+</p>
+!! end
+
+!! test
+External links: encoded ampersand
+!! input
+Old &amp; use: http://x&amp;y
+!! result
+<p>Old &amp; use: <a rel="nofollow" class="external free" href="http://x&amp;y">http://x&amp;y</a>
+</p>
+!! end
+
+!! test
+External links: encoded equals (bug 6102)
+!! input
+http://example.com/?foo&#61;bar
+!! result
+<p><a rel="nofollow" class="external free" href="http://example.com/?foo=bar">http://example.com/?foo=bar</a>
+</p>
+!! end
+
+!! test
+External links: [raw ampersand]
+!! input
+Old &amp; use: [http://x&y]
+!! result
+<p>Old &amp; use: <a rel="nofollow" class="external autonumber" href="http://x&amp;y">[1]</a>
+</p>
+!! end
+
+!! test
+External links: [encoded ampersand]
+!! input
+Old &amp; use: [http://x&amp;y]
+!! result
+<p>Old &amp; use: <a rel="nofollow" class="external autonumber" href="http://x&amp;y">[1]</a>
+</p>
+!! end
+
+!! test
+External links: [encoded equals] (bug 6102)
+!! input
+[http://example.com/?foo&#61;bar]
+!! result
+<p><a rel="nofollow" class="external autonumber" href="http://example.com/?foo=bar">[1]</a>
+</p>
+!! end
+
+!! test
+External links: [IDN ignored character reference in hostname; strip it right off]
+!! input
+[http://e&zwnj;xample.com/]
+!! result
+<p><a rel="nofollow" class="external autonumber" href="http://example.com/">[1]</a>
+</p>
+!! end
+
+!! test
+External links: IDN ignored character reference in hostname; strip it right off
+!! input
+http://e&zwnj;xample.com/
+!! result
+<p><a rel="nofollow" class="external free" href="http://example.com/">http://example.com/</a>
+</p>
+!! end
+
+!! test
+External links: www.jpeg.org (bug 554)
+!! input
+http://www.jpeg.org
+!!result
+<p><a rel="nofollow" class="external free" href="http://www.jpeg.org">http://www.jpeg.org</a>
+</p>
+!! end
+
+!! test
+External links: URL within URL (original bug 2)
+!! input
+[http://www.unausa.org/newindex.asp?place=http://www.unausa.org/programs/mun.asp]
+!! result
+<p><a rel="nofollow" class="external autonumber" href="http://www.unausa.org/newindex.asp?place=http://www.unausa.org/programs/mun.asp">[1]</a>
+</p>
+!! end
+
+!! test
+BUG 361: URL inside bracketed URL
+!! input
+[http://www.example.com/foo http://www.example.com/bar]
+!! result
+<p><a rel="nofollow" class="external text" href="http://www.example.com/foo">http://www.example.com/bar</a>
+</p>
+!! end
+
+!! test
+BUG 361: URL within URL, not bracketed
+!! input
+http://www.example.com/foo?=http://www.example.com/bar
+!! result
+<p><a rel="nofollow" class="external free" href="http://www.example.com/foo?=http://www.example.com/bar">http://www.example.com/foo?=http://www.example.com/bar</a>
+</p>
+!! end
+
+!! test
+BUG 289: ">"-token in URL-tail
+!! input
+http://www.example.com/<hello>
+!! result
+<p><a rel="nofollow" class="external free" href="http://www.example.com/">http://www.example.com/</a>&lt;hello&gt;
+</p>
+!!end
+
+!! test
+BUG 289: literal ">"-token in URL-tail
+!! input
+http://www.example.com/<b>html</b>
+!! result
+<p><a rel="nofollow" class="external free" href="http://www.example.com/">http://www.example.com/</a><b>html</b>
+</p>
+!!end
+
+!! test
+BUG 289: ">"-token in bracketed URL
+!! input
+[http://www.example.com/<hello> stuff]
+!! result
+<p><a rel="nofollow" class="external text" href="http://www.example.com/">&lt;hello&gt; stuff</a>
+</p>
+!!end
+
+!! test
+BUG 289: literal ">"-token in bracketed URL
+!! input
+[http://www.example.com/<b>html</b> stuff]
+!! result
+<p><a rel="nofollow" class="external text" href="http://www.example.com/"><b>html</b> stuff</a>
+</p>
+!!end
+
+!! test
+BUG 289: literal double quote at end of URL
+!! input
+http://www.example.com/"hello"
+!! result
+<p><a rel="nofollow" class="external free" href="http://www.example.com/">http://www.example.com/</a>"hello"
+</p>
+!!end
+
+!! test
+BUG 289: literal double quote in bracketed URL
+!! input
+[http://www.example.com/"hello" stuff]
+!! result
+<p><a rel="nofollow" class="external text" href="http://www.example.com/">"hello" stuff</a>
+</p>
+!!end
+
+!! test
+External links: multiple legal whitespace is fine, Magnus. Don't break it please. (bug 5081)
+!! input
+[http://www.example.com test]
+!! result
+<p><a rel="nofollow" class="external text" href="http://www.example.com">test</a>
+</p>
+!! end
+
+!! test
+External links: wiki links within external link (Bug 3695)
+!! input
+[http://example.com [[wikilink]] embedded in ext link]
+!! result
+<p><a rel="nofollow" class="external text" href="http://example.com"></a><a href="/index.php?title=Wikilink&amp;action=edit&amp;redlink=1" class="new" title="Wikilink (page does not exist)">wikilink</a><a rel="nofollow" class="external text" href="http://example.com"> embedded in ext link</a>
+</p>
+!! end
+
+!! test
+BUG 787: Links with one slash after the url protocol are invalid
+!! input
+http:/example.com
+
+[http:/example.com title]
+!! result
+<p>http:/example.com
+</p><p>[http:/example.com title]
+</p>
+!! end
+
+!! test
+Bug 2702: Mismatched <i>, <b> and <a> tags are invalid
+!! input
+''[http://example.com text'']
+[http://example.com '''text]'''
+''Something [http://example.com in italic'']
+''Something [http://example.com mixed''''', even bold]'''
+'''''Now [http://example.com both''''']
+!! result
+<p><a rel="nofollow" class="external text" href="http://example.com"><i>text</i></a>
+<a rel="nofollow" class="external text" href="http://example.com"><b>text</b></a>
+<i>Something </i><a rel="nofollow" class="external text" href="http://example.com"><i>in italic</i></a>
+<i>Something </i><a rel="nofollow" class="external text" href="http://example.com"><i>mixed</i><b>, even bold</b></a>
+<i><b>Now </b></i><a rel="nofollow" class="external text" href="http://example.com"><i><b>both</b></i></a>
+</p>
+!! end
+
+
+!! test
+Bug 4781: %26 in URL
+!! input
+http://www.example.com/?title=AT%26T
+!! result
+<p><a rel="nofollow" class="external free" href="http://www.example.com/?title=AT%26T">http://www.example.com/?title=AT%26T</a>
+</p>
+!! end
+
+!! test
+Bug 4781, 5267: %26 in URL
+!! input
+http://www.example.com/?title=100%25_Bran
+!! result
+<p><a rel="nofollow" class="external free" href="http://www.example.com/?title=100%25_Bran">http://www.example.com/?title=100%25_Bran</a>
+</p>
+!! end
+
+!! test
+Bug 4781, 5267: %28, %29 in URL
+!! input
+http://www.example.com/?title=Ben-Hur_%281959_film%29
+!! result
+<p><a rel="nofollow" class="external free" href="http://www.example.com/?title=Ben-Hur_%281959_film%29">http://www.example.com/?title=Ben-Hur_%281959_film%29</a>
+</p>
+!! end
+
+
+!! test
+Bug 4781: %26 in autonumber URL
+!! input
+[http://www.example.com/?title=AT%26T]
+!! result
+<p><a rel="nofollow" class="external autonumber" href="http://www.example.com/?title=AT%26T">[1]</a>
+</p>
+!! end
+
+!! test
+Bug 4781, 5267: %26 in autonumber URL
+!! input
+[http://www.example.com/?title=100%25_Bran]
+!! result
+<p><a rel="nofollow" class="external autonumber" href="http://www.example.com/?title=100%25_Bran">[1]</a>
+</p>
+!! end
+
+!! test
+Bug 4781, 5267: %28, %29 in autonumber URL
+!! input
+[http://www.example.com/?title=Ben-Hur_%281959_film%29]
+!! result
+<p><a rel="nofollow" class="external autonumber" href="http://www.example.com/?title=Ben-Hur_%281959_film%29">[1]</a>
+</p>
+!! end
+
+
+!! test
+Bug 4781: %26 in bracketed URL
+!! input
+[http://www.example.com/?title=AT%26T link]
+!! result
+<p><a rel="nofollow" class="external text" href="http://www.example.com/?title=AT%26T">link</a>
+</p>
+!! end
+
+!! test
+Bug 4781, 5267: %26 in bracketed URL
+!! input
+[http://www.example.com/?title=100%25_Bran link]
+!! result
+<p><a rel="nofollow" class="external text" href="http://www.example.com/?title=100%25_Bran">link</a>
+</p>
+!! end
+
+!! test
+Bug 4781, 5267: %28, %29 in bracketed URL
+!! input
+[http://www.example.com/?title=Ben-Hur_%281959_film%29 link]
+!! result
+<p><a rel="nofollow" class="external text" href="http://www.example.com/?title=Ben-Hur_%281959_film%29">link</a>
+</p>
+!! end
+
+!! test
+External link containing double-single-quotes in text '' (bug 4598 sanity check)
+!! input
+Some [http://example.com/ pretty ''italics'' and stuff]!
+!! result
+<p>Some <a rel="nofollow" class="external text" href="http://example.com/">pretty <i>italics</i> and stuff</a>!
+</p>
+!! end
+
+!! test
+External link containing double-single-quotes in text embedded in italics (bug 4598 sanity check)
+!! input
+''Some [http://example.com/ pretty ''italics'' and stuff]!''
+!! result
+<p><i>Some </i><a rel="nofollow" class="external text" href="http://example.com/"><i>pretty </i>italics<i> and stuff</i></a><i>!</i>
+</p>
+!! end
+
+!! test
+External link containing double-single-quotes with no space separating the url from text in italics
+!! input
+[http://www.musee-picasso.fr/pages/page_id18528_u1l2.htm''La muerte de Casagemas'' (1901) en el sitio de [[Museo Picasso (París)|Museo Picasso]].]
+!! result
+<p><a rel="nofollow" class="external text" href="http://www.musee-picasso.fr/pages/page_id18528_u1l2.htm"><i>La muerte de Casagemas</i> (1901) en el sitio de <a href="/index.php?title=Museo_Picasso_(Par%C3%ADs)&amp;action=edit&amp;redlink=1" class="new" title="Museo Picasso (París) (page does not exist)">Museo Picasso</a>.</a>
+</p>
+!! end
+
+!! test
+URL-encoding in URL functions (single parameter)
+!! input
+{{localurl:Some page|amp=&}}
+!! result
+<p>/index.php?title=Some_page&amp;amp=&amp;
+</p>
+!! end
+
+!! test
+URL-encoding in URL functions (multiple parameters)
+!! input
+{{localurl:Some page|q=?&amp=&}}
+!! result
+<p>/index.php?title=Some_page&amp;q=?&amp;amp=&amp;
+</p>
+!! end
+
+###
+### Quotes
+###
+
+!! test
+Quotes
+!! input
+Normal text. '''Bold text.''' Normal text. ''Italic text.''
+
+Normal text. '''''Bold italic text.''''' Normal text.
+!!result
+<p>Normal text. <b>Bold text.</b> Normal text. <i>Italic text.</i>
+</p><p>Normal text. <i><b>Bold italic text.</b></i> Normal text.
+</p>
+!! end
+
+
+!! test
+Unclosed and unmatched quotes
+!! input
+'''''Bold italic text '''with bold deactivated''' in between.'''''
+
+'''''Bold italic text ''with italic deactivated'' in between.'''''
+
+'''Bold text..
+
+..spanning two paragraphs (should not work).'''
+
+'''Bold tag left open
+
+''Italic tag left open
+
+Normal text.
+
+<!-- Unmatching number of opening, closing tags: -->
+'''This year''''s election ''should'' beat '''last year''''s.
+
+''Tom'''s car is bigger than ''Susan'''s.
+!! result
+<p><i><b>Bold italic text </b>with bold deactivated<b> in between.</b></i>
+</p><p><b><i>Bold italic text </i>with italic deactivated<i> in between.</i></b>
+</p><p><b>Bold text..</b>
+</p><p>..spanning two paragraphs (should not work).
+</p><p><b>Bold tag left open</b>
+</p><p><i>Italic tag left open</i>
+</p><p>Normal text.
+</p><p><b>This year'</b>s election <i>should</i> beat <b>last year'</b>s.
+</p><p><i>Tom<b>s car is bigger than </b></i><b>Susan</b>s.
+</p>
+!! end
+
+###
+### Tables
+###
+### some content taken from http://meta.wikimedia.org/wiki/MediaWiki_User%27s_Guide:_Using_tables
+###
+
+# This should not produce <table></table> as <table><tr><td></td></tr></table>
+# is the bare minimun required by the spec, see:
+# http://www.w3.org/TR/xhtml-modularization/dtd_module_defs.html#a_module_Basic_Tables
+!! test
+A table with no data.
+!! input
+{||}
+!! result
+!! end
+
+# A table with nothing but a caption is invalid XHTML, we might want to render
+# this as <p>caption</p>
+!! test
+A table with nothing but a caption
+!! input
+{|
+|+ caption
+|}
+!! result
+<table>
+<caption> caption
+</caption><tr><td></td></tr></table>
+
+!! end
+
+!! test
+Simple table
+!! input
+{|
+| 1 || 2
+|-
+| 3 || 4
+|}
+!! result
+<table>
+<tr>
+<td> 1 </td>
+<td> 2
+</td></tr>
+<tr>
+<td> 3 </td>
+<td> 4
+</td></tr></table>
+
+!! end
+
+!! test
+Multiplication table
+!! input
+{| border="1" cellpadding="2"
+|+Multiplication table
+|-
+! &times; !! 1 !! 2 !! 3
+|-
+! 1
+| 1 || 2 || 3
+|-
+! 2
+| 2 || 4 || 6
+|-
+! 3
+| 3 || 6 || 9
+|-
+! 4
+| 4 || 8 || 12
+|-
+! 5
+| 5 || 10 || 15
+|}
+!! result
+<table border="1" cellpadding="2">
+<caption>Multiplication table
+</caption>
+<tr>
+<th> &#215; </th>
+<th> 1 </th>
+<th> 2 </th>
+<th> 3
+</th></tr>
+<tr>
+<th> 1
+</th>
+<td> 1 </td>
+<td> 2 </td>
+<td> 3
+</td></tr>
+<tr>
+<th> 2
+</th>
+<td> 2 </td>
+<td> 4 </td>
+<td> 6
+</td></tr>
+<tr>
+<th> 3
+</th>
+<td> 3 </td>
+<td> 6 </td>
+<td> 9
+</td></tr>
+<tr>
+<th> 4
+</th>
+<td> 4 </td>
+<td> 8 </td>
+<td> 12
+</td></tr>
+<tr>
+<th> 5
+</th>
+<td> 5 </td>
+<td> 10 </td>
+<td> 15
+</td></tr></table>
+
+!! end
+
+!! test
+Table rowspan
+!! input
+{| align=right border=1
+| Cell 1, row 1
+|rowspan=2| Cell 2, row 1 (and 2)
+| Cell 3, row 1
+|-
+| Cell 1, row 2
+| Cell 3, row 2
+|}
+!! result
+<table align="right" border="1">
+<tr>
+<td> Cell 1, row 1
+</td>
+<td rowspan="2"> Cell 2, row 1 (and 2)
+</td>
+<td> Cell 3, row 1
+</td></tr>
+<tr>
+<td> Cell 1, row 2
+</td>
+<td> Cell 3, row 2
+</td></tr></table>
+
+!! end
+
+!! test
+Nested table
+!! input
+{| border=1
+| &alpha;
+|
+{| bgcolor=#ABCDEF border=2
+|nested
+|-
+|table
+|}
+|the original table again
+|}
+!! result
+<table border="1">
+<tr>
+<td> &#945;
+</td>
+<td>
+<table bgcolor="#ABCDEF" border="2">
+<tr>
+<td>nested
+</td></tr>
+<tr>
+<td>table
+</td></tr></table>
+</td>
+<td>the original table again
+</td></tr></table>
+
+!! end
+
+!! test
+Invalid attributes in table cell (bug 1830)
+!! input
+{|
+|Cell:|broken
+|}
+!! result
+<table>
+<tr>
+<td>broken
+</td></tr></table>
+
+!! end
+
+
+!! test
+Table security: embedded pipes (http://lists.wikimedia.org/mailman/htdig/wikitech-l/2006-April/022293.html)
+!! input
+{|
+| |[ftp://|x||]" onmouseover="alert(document.cookie)">test
+!! result
+<table>
+<tr>
+<td>[<a rel="nofollow" class="external free" href="ftp://%7Cx">ftp://%7Cx</a></td>
+<td>]" onmouseover="alert(document.cookie)"&gt;test
+</td>
+</tr>
+</table>
+
+!! end
+
+
+
+###
+### Internal links
+###
+!! test
+Plain link, capitalized
+!! input
+[[Main Page]]
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">Main Page</a>
+</p>
+!! end
+
+!! test
+Plain link, uncapitalized
+!! input
+[[main Page]]
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">main Page</a>
+</p>
+!! end
+
+!! test
+Piped link
+!! input
+[[Main Page|The Main Page]]
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">The Main Page</a>
+</p>
+!! end
+
+!! test
+Broken link
+!! input
+[[Zigzagzogzagzig]]
+!! result
+<p><a href="/index.php?title=Zigzagzogzagzig&amp;action=edit&amp;redlink=1" class="new" title="Zigzagzogzagzig (page does not exist)">Zigzagzogzagzig</a>
+</p>
+!! end
+
+!! test
+Broken link with fragment
+!! input
+[[Zigzagzogzagzig#zug]]
+!! result
+<p><a href="/index.php?title=Zigzagzogzagzig&amp;action=edit&amp;redlink=1" class="new" title="Zigzagzogzagzig (page does not exist)">Zigzagzogzagzig#zug</a>
+</p>
+!! end
+
+!! test
+Special page link with fragment
+!! input
+[[Special:Version#anchor]]
+!! result
+<p><a href="/wiki/Special:Version#anchor" title="Special:Version">Special:Version#anchor</a>
+</p>
+!! end
+
+!! test
+Nonexistent special page link with fragment
+!! input
+[[Special:ThisNameWillHopefullyNeverBeUsed#anchor]]
+!! result
+<p><a href="/wiki/Special:ThisNameWillHopefullyNeverBeUsed" class="new" title="Special:ThisNameWillHopefullyNeverBeUsed (page does not exist)">Special:ThisNameWillHopefullyNeverBeUsed#anchor</a>
+</p>
+!! end
+
+!! test
+Link with prefix
+!! input
+xxx[[main Page]], xxx[[Main Page]], Xxx[[main Page]] XXX[[main Page]], XXX[[Main Page]]
+!! result
+<p>xxx<a href="/wiki/Main_Page" title="Main Page">main Page</a>, xxx<a href="/wiki/Main_Page" title="Main Page">Main Page</a>, Xxx<a href="/wiki/Main_Page" title="Main Page">main Page</a> XXX<a href="/wiki/Main_Page" title="Main Page">main Page</a>, XXX<a href="/wiki/Main_Page" title="Main Page">Main Page</a>
+</p>
+!! end
+
+!! test
+Link with suffix
+!! input
+[[Main Page]]xxx, [[Main Page]]XXX, [[Main Page]]!!!
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">Main Pagexxx</a>, <a href="/wiki/Main_Page" title="Main Page">Main Page</a>XXX, <a href="/wiki/Main_Page" title="Main Page">Main Page</a>!!!
+</p>
+!! end
+
+!! test
+Link with 3 brackets
+!! input
+[[[main page]]]
+!! result
+<p>[[[main page]]]
+</p>
+!! end
+
+!! test
+Piped link with 3 brackets
+!! input
+[[[main page|the main page]]]
+!! result
+<p>[[[main page|the main page]]]
+</p>
+!! end
+
+!! test
+Link with multiple pipes
+!! input
+[[Main Page|The|Main|Page]]
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">The|Main|Page</a>
+</p>
+!! end
+
+!! test
+Link to namespaces
+!! input
+[[Talk:Parser testing]], [[Meta:Disclaimers]]
+!! result
+<p><a href="/index.php?title=Talk:Parser_testing&amp;action=edit&amp;redlink=1" class="new" title="Talk:Parser testing (page does not exist)">Talk:Parser testing</a>, <a href="/index.php?title=Meta:Disclaimers&amp;action=edit&amp;redlink=1" class="new" title="Meta:Disclaimers (page does not exist)">Meta:Disclaimers</a>
+</p>
+!! end
+
+!! test
+Piped link to namespace
+!! input
+[[Meta:Disclaimers|The disclaimers]]
+!! result
+<p><a href="/index.php?title=Meta:Disclaimers&amp;action=edit&amp;redlink=1" class="new" title="Meta:Disclaimers (page does not exist)">The disclaimers</a>
+</p>
+!! end
+
+!! test
+Link containing }
+!! input
+[[Usually caused by a typo (oops}]]
+!! result
+<p>[[Usually caused by a typo (oops}]]
+</p>
+!! end
+
+!! test
+Link containing % (not as a hex sequence)
+!! input
+[[7% Solution]]
+!! result
+<p><a href="/index.php?title=7%25_Solution&amp;action=edit&amp;redlink=1" class="new" title="7% Solution (page does not exist)">7% Solution</a>
+</p>
+!! end
+
+!! test
+Link containing % as a single hex sequence interpreted to char
+!! input
+[[7%25 Solution]]
+!! result
+<p><a href="/index.php?title=7%25_Solution&amp;action=edit&amp;redlink=1" class="new" title="7% Solution (page does not exist)">7% Solution</a>
+</p>
+!!end
+
+!! test
+Link containing % as a double hex sequence interpreted to hex sequence
+!! input
+[[7%2525 Solution]]
+!! result
+<p>[[7%2525 Solution]]
+</p>
+!!end
+
+!! test
+Link containing "#<" and "#>" % as a hex sequences- these are valid section anchors
+Example for such a section: == < ==
+!! input
+[[%23%3c]][[%23%3e]]
+!! result
+<p><a href="#.3C">#&lt;</a><a href="#.3E">#&gt;</a>
+</p>
+!! end
+
+!! test
+Link containing "<#" and ">#" as a hex sequences
+!! input
+[[%3c%23]][[%3e%23]]
+!! result
+<p>[[%3c%23]][[%3e%23]]
+</p>
+!! end
+
+!! test
+Link containing double-single-quotes '' (bug 4598)
+!! input
+[[Lista d''e paise d''o munno]]
+!! result
+<p><a href="/index.php?title=Lista_d%27%27e_paise_d%27%27o_munno&amp;action=edit&amp;redlink=1" class="new" title="Lista d''e paise d''o munno (page does not exist)">Lista d''e paise d''o munno</a>
+</p>
+!! end
+
+!! test
+Link containing double-single-quotes '' in text (bug 4598 sanity check)
+!! input
+Some [[Link|pretty ''italics'' and stuff]]!
+!! result
+<p>Some <a href="/index.php?title=Link&amp;action=edit&amp;redlink=1" class="new" title="Link (page does not exist)">pretty <i>italics</i> and stuff</a>!
+</p>
+!! end
+
+!! test
+Link containing double-single-quotes '' in text embedded in italics (bug 4598 sanity check)
+!! input
+''Some [[Link|pretty ''italics'' and stuff]]!
+!! result
+<p><i>Some <a href="/index.php?title=Link&amp;action=edit&amp;redlink=1" class="new" title="Link (page does not exist)">pretty <i>italics</i> and stuff</a>!</i>
+</p>
+!! end
+
+!! test
+Link with double quotes in title part (literal) and alternate part (interpreted)
+!! input
+[[File:Denys Savchenko ''Pentecoste''.jpg]]
+
+[[''Pentecoste'']]
+
+[[''Pentecoste''|Pentecoste]]
+
+[[''Pentecoste''|''Pentecoste'']]
+!! result
+<p><a href="/index.php?title=Special:Upload&amp;wpDestFile=Denys_Savchenko_%27%27Pentecoste%27%27.jpg" class="new" title="File:Denys Savchenko &#39;&#39;Pentecoste&#39;&#39;.jpg">File:Denys Savchenko <i>Pentecoste</i>.jpg</a>
+</p><p><a href="/index.php?title=%27%27Pentecoste%27%27&amp;action=edit&amp;redlink=1" class="new" title="''Pentecoste'' (page does not exist)">''Pentecoste''</a>
+</p><p><a href="/index.php?title=%27%27Pentecoste%27%27&amp;action=edit&amp;redlink=1" class="new" title="''Pentecoste'' (page does not exist)">Pentecoste</a>
+</p><p><a href="/index.php?title=%27%27Pentecoste%27%27&amp;action=edit&amp;redlink=1" class="new" title="''Pentecoste'' (page does not exist)"><i>Pentecoste</i></a>
+</p>
+!! end
+
+!! test
+Plain link to URL
+!! input
+[[http://www.example.com]]
+!! result
+<p>[<a rel="nofollow" class="external autonumber" href="http://www.example.com">[1]</a>]
+</p>
+!! end
+
+!! test
+Plain link to URL with link text
+!! input
+[[http://www.example.com Link text]]
+!! result
+<p>[<a rel="nofollow" class="external text" href="http://www.example.com">Link text</a>]
+</p>
+!! end
+
+!! test
+Plain link to protocol-relative URL
+!! input
+[[//www.example.com]]
+!! result
+<p>[<a rel="nofollow" class="external autonumber" href="//www.example.com">[1]</a>]
+</p>
+!! end
+
+!! test
+Plain link to protocol-relative URL with link text
+!! input
+[[//www.example.com Link text]]
+!! result
+<p>[<a rel="nofollow" class="external text" href="//www.example.com">Link text</a>]
+</p>
+!! end
+
+
+# I'm fairly sure the expected result here is wrong.
+# We want these to be URL links, not pseudo-pages with URLs for titles....
+# However the current output is also pretty screwy.
+#
+# ----
+# I'm changing it to match the current output--it arguably makes more
+# sense in the light of the test above. Old expected result was:
+#<p>Piped link to URL: <a href="/index.php?title=Http://www.example.com&amp;action=edit" class="new">an example URL</a>
+#</p>
+# But I think this test is bordering on "garbage in, garbage out" anyway.
+# -- wtm
+!! test
+Piped link to URL
+!! input
+Piped link to URL: [[http://www.example.com|an example URL]]
+!! result
+<p>Piped link to URL: [<a rel="nofollow" class="external text" href="http://www.example.com%7Can">example URL</a>]
+</p>
+!! end
+
+!! test
+BUG 2: [[page|http://url/]] should link to page, not http://url/
+!! input
+[[Main Page|http://url/]]
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">http://url/</a>
+</p>
+!! end
+
+!! test
+BUG 337: Escaped self-links should be bold
+!! options
+title=[[Bug462]]
+!! input
+[[Bu&#103;462]] [[Bug462]]
+!! result
+<p><strong class="selflink">Bu&#103;462</strong> <strong class="selflink">Bug462</strong>
+</p>
+!! end
+
+!! test
+Self-link to section should not be bold
+!! options
+title=[[Main Page]]
+!! input
+[[Main Page#section]]
+!! result
+<p><a href="/wiki/Main_Page#section" title="Main Page">Main Page#section</a>
+</p>
+!! end
+
+!! article
+00
+!! text
+This is 00.
+!! endarticle
+
+!!test
+Self-link to numeric title
+!!options
+title=[[0]]
+!!input
+[[0]]
+!!result
+<p><strong class="selflink">0</strong>
+</p>
+!!end
+
+!!test
+Link to numeric-equivalent title
+!!options
+title=[[0]]
+!!input
+[[00]]
+!!result
+<p><a href="/wiki/00" title="00">00</a>
+</p>
+!!end
+
+!! test
+<nowiki> inside a link
+!! input
+[[Main<nowiki> Page</nowiki>]] [[Main Page|the main page <nowiki>[it's not very good]</nowiki>]]
+!! result
+<p>[[Main Page]] <a href="/wiki/Main_Page" title="Main Page">the main page [it's not very good]</a>
+</p>
+!! end
+
+!! test
+Non-breaking spaces in title
+!! input
+[[&nbsp; Main &nbsp; Page &nbsp;]]
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">&#160; Main &#160; Page &#160;</a>
+</p>
+!!end
+
+!! test
+Internal link with ca linktrail, surrounded by bold apostrophes (bug 27473 primary issue)
+!! options
+language=ca
+!! input
+'''[[Main Page]]'''
+!! result
+<p><b><a href="/wiki/Main_Page" title="Main Page">Main Page</a></b>
+</p>
+!! end
+
+!! test
+Internal link with ca linktrail, surrounded by italic apostrophes (bug 27473 primary issue)
+!! options
+language=ca
+!! input
+''[[Main Page]]''
+!! result
+<p><i><a href="/wiki/Main_Page" title="Main Page">Main Page</a></i>
+</p>
+!! end
+
+!! test
+Internal link with en linktrail: no apostrophes (bug 27473)
+!! options
+language=en
+!! input
+[[Something]]'nice
+!! result
+<p><a href="/index.php?title=Something&amp;action=edit&amp;redlink=1" class="new" title="Something (page does not exist)">Something</a>'nice
+</p>
+!! end
+
+!! test
+Internal link with ca linktrail with apostrophes (bug 27473)
+!! options
+language=ca
+!! input
+[[Something]]'nice
+!! result
+<p><a href="/index.php?title=Something&amp;action=edit&amp;redlink=1" class="new" title="Something (encara no existeix)">Something'nice</a>
+</p>
+!! end
+
+!! test
+Internal link with kaa linktrail with apostrophes (bug 27473)
+!! options
+language=kaa
+!! input
+[[Something]]'nice
+!! result
+<p><a href="/index.php?title=Something&amp;action=edit&amp;redlink=1" class="new" title="Something (bet ele jaratılmag'an)">Something'nice</a>
+</p>
+!! end
+
+###
+### Interwiki links (see maintenance/interwiki.sql)
+###
+
+!! test
+Inline interwiki link
+!! input
+[[MeatBall:SoftSecurity]]
+!! result
+<p><a href="http://www.usemod.com/cgi-bin/mb.pl?SoftSecurity" class="extiw" title="meatball:SoftSecurity">MeatBall:SoftSecurity</a>
+</p>
+!! end
+
+!! test
+Inline interwiki link with empty title (bug 2372)
+!! input
+[[MeatBall:]]
+!! result
+<p><a href="http://www.usemod.com/cgi-bin/mb.pl?" class="extiw" title="meatball:">MeatBall:</a>
+</p>
+!! end
+
+!! test
+Interwiki link encoding conversion (bug 1636)
+!! input
+*[[Wikipedia:ro:Olteni&#0355;a]]
+*[[Wikipedia:ro:Olteni&#355;a]]
+!! result
+<ul><li><a href="http://en.wikipedia.org/wiki/ro:Olteni%C5%A3a" class="extiw" title="wikipedia:ro:Olteniţa">Wikipedia:ro:Olteni&#355;a</a>
+</li><li><a href="http://en.wikipedia.org/wiki/ro:Olteni%C5%A3a" class="extiw" title="wikipedia:ro:Olteniţa">Wikipedia:ro:Olteni&#355;a</a>
+</li></ul>
+
+!! end
+
+!! test
+Interwiki link with fragment (bug 2130)
+!! input
+[[MeatBall:SoftSecurity#foo]]
+!! result
+<p><a href="http://www.usemod.com/cgi-bin/mb.pl?SoftSecurity#foo" class="extiw" title="meatball:SoftSecurity">MeatBall:SoftSecurity#foo</a>
+</p>
+!! end
+
+!! test
+Interlanguage link
+!! input
+Blah blah blah
+[[zh:Chinese]]
+!!result
+<p>Blah blah blah
+</p>
+!! end
+
+!! test
+Double interlanguage link
+!! input
+Blah blah blah
+[[es:Spanish]]
+[[zh:Chinese]]
+!!result
+<p>Blah blah blah
+</p>
+!! end
+
+!! test
+Interlanguage link, with prefix links
+!! options
+language=ln
+!! input
+Blah blah blah
+[[zh:Chinese]]
+!!result
+<p>Blah blah blah
+</p>
+!! end
+
+!! test
+Double interlanguage link, with prefix links (bug 8897)
+!! options
+language=ln
+!! input
+Blah blah blah
+[[es:Spanish]]
+[[zh:Chinese]]
+!!result
+<p>Blah blah blah
+</p>
+!! end
+
+
+##
+## XHTML tidiness
+###
+
+!! test
+<br> to <br />
+!! input
+1<br>2<br />3
+!! result
+<p>1<br />2<br />3
+</p>
+!! end
+
+!! test
+Incorrecly removing closing slashes from correctly formed XHTML
+!! input
+<br style="clear:both;" />
+!! result
+<p><br style="clear:both;" />
+</p>
+!! end
+
+!! test
+Failing to transform badly formed HTML into correct XHTML
+!! input
+<br clear=left>
+<br clear=right>
+<br clear=all>
+!! result
+<p><br clear="left" />
+<br clear="right" />
+<br clear="all" />
+</p>
+!!end
+
+!! test
+Horizontal ruler (should it add that extra space?)
+!! input
+<hr>
+<hr >
+foo <hr
+> bar
+!! result
+<hr />
+<hr />
+foo <hr /> bar
+
+!! end
+
+###
+### Block-level elements
+###
+!! test
+Common list
+!! input
+*Common list
+* item 2
+*item 3
+!! result
+<ul><li>Common list
+</li><li> item 2
+</li><li>item 3
+</li></ul>
+
+!! end
+
+!! test
+Numbered list
+!! input
+#Numbered list
+#item 2
+# item 3
+!! result
+<ol><li>Numbered list
+</li><li>item 2
+</li><li> item 3
+</li></ol>
+
+!! end
+
+!! test
+Mixed list
+!! input
+*Mixed list
+*# with numbers
+** and bullets
+*# and numbers
+*bullets again
+**bullet level 2
+***bullet level 3
+***#Number on level 4
+**bullet level 2
+**#Number on level 3
+**#Number on level 3
+*#number level 2
+*Level 1
+!! result
+<ul><li>Mixed list
+<ol><li> with numbers
+</li></ol>
+<ul><li> and bullets
+</li></ul>
+<ol><li> and numbers
+</li></ol>
+</li><li>bullets again
+<ul><li>bullet level 2
+<ul><li>bullet level 3
+<ol><li>Number on level 4
+</li></ol>
+</li></ul>
+</li><li>bullet level 2
+<ol><li>Number on level 3
+</li><li>Number on level 3
+</li></ol>
+</li></ul>
+<ol><li>number level 2
+</li></ol>
+</li><li>Level 1
+</li></ul>
+
+!! end
+
+!! test
+List items are not parsed correctly following a <pre> block (bug 785)
+!! input
+* <pre>foo</pre>
+* <pre>bar</pre>
+* zar
+!! result
+<ul><li> <pre>foo</pre>
+</li><li> <pre>bar</pre>
+</li><li> zar
+</li></ul>
+
+!! end
+
+###
+### Magic Words
+###
+
+!! test
+Magic Word: {{CURRENTDAY}}
+!! input
+{{CURRENTDAY}}
+!! result
+<p>1
+</p>
+!! end
+
+!! test
+Magic Word: {{CURRENTDAY2}}
+!! input
+{{CURRENTDAY2}}
+!! result
+<p>01
+</p>
+!! end
+
+!! test
+Magic Word: {{CURRENTDAYNAME}}
+!! input
+{{CURRENTDAYNAME}}
+!! result
+<p>Thursday
+</p>
+!! end
+
+!! test
+Magic Word: {{CURRENTDOW}}
+!! input
+{{CURRENTDOW}}
+!! result
+<p>4
+</p>
+!! end
+
+!! test
+Magic Word: {{CURRENTMONTH}}
+!! input
+{{CURRENTMONTH}}
+!! result
+<p>01
+</p>
+!! end
+
+!! test
+Magic Word: {{CURRENTMONTHABBREV}}
+!! input
+{{CURRENTMONTHABBREV}}
+!! result
+<p>Jan
+</p>
+!! end
+
+!! test
+Magic Word: {{CURRENTMONTHNAME}}
+!! input
+{{CURRENTMONTHNAME}}
+!! result
+<p>January
+</p>
+!! end
+
+!! test
+Magic Word: {{CURRENTMONTHNAMEGEN}}
+!! input
+{{CURRENTMONTHNAMEGEN}}
+!! result
+<p>January
+</p>
+!! end
+
+!! test
+Magic Word: {{CURRENTTIME}}
+!! input
+{{CURRENTTIME}}
+!! result
+<p>00:02
+</p>
+!! end
+
+!! test
+Magic Word: {{CURRENTWEEK}} (@bug 4594)
+!! input
+{{CURRENTWEEK}}
+!! result
+<p>1
+</p>
+!! end
+
+!! test
+Magic Word: {{CURRENTYEAR}}
+!! input
+{{CURRENTYEAR}}
+!! result
+<p>1970
+</p>
+!! end
+
+!! test
+Magic Word: {{FULLPAGENAME}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{FULLPAGENAME}}
+!! result
+<p>User:Ævar Arnfjörð Bjarmason
+</p>
+!! end
+
+!! test
+Magic Word: {{FULLPAGENAMEE}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{FULLPAGENAMEE}}
+!! result
+<p>User:%C3%86var_Arnfj%C3%B6r%C3%B0_Bjarmason
+</p>
+!! end
+
+!! test
+Magic Word: {{NAMESPACE}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{NAMESPACE}}
+!! result
+<p>User
+</p>
+!! end
+
+!! test
+Magic Word: {{NAMESPACEE}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{NAMESPACEE}}
+!! result
+<p>User
+</p>
+!! end
+
+!! test
+Magic Word: {{NUMBEROFFILES}}
+!! input
+{{NUMBEROFFILES}}
+!! result
+<p>2
+</p>
+!! end
+
+!! test
+Magic Word: {{PAGENAME}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{PAGENAME}}
+!! result
+<p>Ævar Arnfjörð Bjarmason
+</p>
+!! end
+
+!! test
+Magic Word: {{PAGENAME}} with metacharacters
+!! options
+title=[['foo & bar = baz']]
+!! input
+''{{PAGENAME}}''
+!! result
+<p><i>&#39;foo &#38; bar &#61; baz&#39;</i>
+</p>
+!! end
+
+!! test
+Magic Word: {{PAGENAME}} with metacharacters (bug 26781)
+!! options
+title=[[*RFC 1234 http://example.com/]]
+!! input
+{{PAGENAME}}
+!! result
+<p>&#42;RFC&#32;1234 http&#58;//example.com/
+</p>
+!! end
+
+!! test
+Magic Word: {{PAGENAMEE}}
+!! options
+title=[[User:Ævar Arnfjörð Bjarmason]]
+!! input
+{{PAGENAMEE}}
+!! result
+<p>%C3%86var_Arnfj%C3%B6r%C3%B0_Bjarmason
+</p>
+!! end
+
+!! test
+Magic Word: {{PAGENAMEE}} with metacharacters (bug 26781)
+!! options
+title=[[*RFC 1234 http://example.com/]]
+!! input
+{{PAGENAMEE}}
+!! result
+<p>&#42;RFC_1234_http&#58;//example.com/
+</p>
+!! end
+
+!! test
+Magic Word: {{REVISIONID}}
+!! input
+{{REVISIONID}}
+!! result
+<p>1337
+</p>
+!! end
+
+!! test
+Magic Word: {{SCRIPTPATH}}
+!! input
+{{SCRIPTPATH}}
+!! result
+<p>/
+</p>
+!! end
+
+!! test
+Magic Word: {{SERVER}}
+!! input
+{{SERVER}}
+!! result
+<p><a rel="nofollow" class="external free" href="http://Britney-Spears">http://Britney-Spears</a>
+</p>
+!! end
+
+!! test
+Magic Word: {{SERVERNAME}}
+!! input
+{{SERVERNAME}}
+!! result
+<p>Britney-Spears
+</p>
+!! end
+
+!! test
+Magic Word: {{SITENAME}}
+!! input
+{{SITENAME}}
+!! result
+<p>MediaWiki
+</p>
+!! end
+
+!! test
+Namespace 1 {{ns:1}}
+!! input
+{{ns:1}}
+!! result
+<p>Talk
+</p>
+!! end
+
+!! test
+Namespace 1 {{ns:01}}
+!! input
+{{ns:01}}
+!! result
+<p>Talk
+</p>
+!! end
+
+!! test
+Namespace 0 {{ns:0}} (bug 4783)
+!! input
+{{ns:0}}
+!! result
+
+!! end
+
+!! test
+Namespace 0 {{ns:00}} (bug 4783)
+!! input
+{{ns:00}}
+!! result
+
+!! end
+
+!! test
+Namespace -1 {{ns:-1}}
+!! input
+{{ns:-1}}
+!! result
+<p>Special
+</p>
+!! end
+
+!! test
+Namespace User {{ns:User}}
+!! input
+{{ns:User}}
+!! result
+<p>User
+</p>
+!! end
+
+!! test
+Namespace User talk {{ns:User_talk}}
+!! input
+{{ns:User_talk}}
+!! result
+<p>User talk
+</p>
+!! end
+
+!! test
+Namespace User talk {{ns:uSeR tAlK}}
+!! input
+{{ns:uSeR tAlK}}
+!! result
+<p>User talk
+</p>
+!! end
+
+!! test
+Namespace File {{ns:File}}
+!! input
+{{ns:File}}
+!! result
+<p>File
+</p>
+!! end
+
+!! test
+Namespace File {{ns:Image}}
+!! input
+{{ns:Image}}
+!! result
+<p>File
+</p>
+!! end
+
+!! test
+Namespace (lang=de) Benutzer {{ns:User}}
+!! options
+language=de
+!! input
+{{ns:User}}
+!! result
+<p>Benutzer
+</p>
+!! end
+
+!! test
+Namespace (lang=de) Benutzer Diskussion {{ns:3}}
+!! options
+language=de
+!! input
+{{ns:3}}
+!! result
+<p>Benutzer Diskussion
+</p>
+!! end
+
+
+!! test
+Urlencode
+!! input
+{{urlencode:hi world?!}}
+{{urlencode:hi world?!|WIKI}}
+{{urlencode:hi world?!|PATH}}
+{{urlencode:hi world?!|QUERY}}
+!! result
+<p>hi+world%3F%21
+hi_world%3F!
+hi%20world%3F%21
+hi+world%3F%21
+</p>
+!! end
+
+###
+### Magic links
+###
+!! test
+Magic links: internal link to RFC (bug 479)
+!! input
+[[RFC 123]]
+!! result
+<p><a href="/index.php?title=RFC_123&amp;action=edit&amp;redlink=1" class="new" title="RFC 123 (page does not exist)">RFC 123</a>
+</p>
+!! end
+
+!! test
+Magic links: RFC (bug 479)
+!! input
+RFC 822
+!! result
+<p><a class="external mw-magiclink-rfc" href="//tools.ietf.org/html/rfc822">RFC 822</a>
+</p>
+!! end
+
+!! test
+Magic links: ISBN (bug 1937)
+!! input
+ISBN 0-306-40615-2
+!! result
+<p><a href="/wiki/Special:BookSources/0306406152" class="internal mw-magiclink-isbn">ISBN 0-306-40615-2</a>
+</p>
+!! end
+
+!! test
+Magic links: PMID incorrectly converts space to underscore
+!! input
+PMID 1234
+!! result
+<p><a class="external mw-magiclink-pmid" href="//www.ncbi.nlm.nih.gov/pubmed/1234?dopt=Abstract">PMID 1234</a>
+</p>
+!! end
+
+###
+### Templates
+####
+
+!! test
+Nonexistent template
+!! input
+{{thistemplatedoesnotexist}}
+!! result
+<p><a href="/index.php?title=Template:Thistemplatedoesnotexist&amp;action=edit&amp;redlink=1" class="new" title="Template:Thistemplatedoesnotexist (page does not exist)">Template:Thistemplatedoesnotexist</a>
+</p>
+!! end
+
+!! article
+Template:test
+!! text
+This is a test template
+!! endarticle
+
+!! test
+Simple template
+!! input
+{{test}}
+!! result
+<p>This is a test template
+</p>
+!! end
+
+!! test
+Template with explicit namespace
+!! input
+{{Template:test}}
+!! result
+<p>This is a test template
+</p>
+!! end
+
+
+!! article
+Template:paramtest
+!! text
+This is a test template with parameter {{{param}}}
+!! endarticle
+
+!! test
+Template parameter
+!! input
+{{paramtest|param=foo}}
+!! result
+<p>This is a test template with parameter foo
+</p>
+!! end
+
+!! article
+Template:paramtestnum
+!! text
+[[{{{1}}}|{{{2}}}]]
+!! endarticle
+
+!! test
+Template unnamed parameter
+!! input
+{{paramtestnum|Main Page|the main page}}
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">the main page</a>
+</p>
+!! end
+
+!! article
+Template:templatesimple
+!! text
+(test)
+!! endarticle
+
+!! article
+Template:templateredirect
+!! text
+#redirect [[Template:templatesimple]]
+!! endarticle
+
+!! article
+Template:templateasargtestnum
+!! text
+{{{{{1}}}}}
+!! endarticle
+
+!! article
+Template:templateasargtest
+!! text
+{{template{{{templ}}}}}
+!! endarticle
+
+!! article
+Template:templateasargtest2
+!! text
+{{{{{templ}}}}}
+!! endarticle
+
+!! test
+Template with template name as unnamed argument
+!! input
+{{templateasargtestnum|templatesimple}}
+!! result
+<p>(test)
+</p>
+!! end
+
+!! test
+Template with template name as argument
+!! input
+{{templateasargtest|templ=simple}}
+!! result
+<p>(test)
+</p>
+!! end
+
+!! test
+Template with template name as argument (2)
+!! input
+{{templateasargtest2|templ=templatesimple}}
+!! result
+<p>(test)
+</p>
+!! end
+
+!! article
+Template:templateasargtestdefault
+!! text
+{{{{{templ|templatesimple}}}}}
+!! endarticle
+
+!! article
+Template:templa
+!! text
+'''templ'''
+!! endarticle
+
+!! test
+Template with default value
+!! input
+{{templateasargtestdefault}}
+!! result
+<p>(test)
+</p>
+!! end
+
+!! test
+Template with default value (value set)
+!! input
+{{templateasargtestdefault|templ=templa}}
+!! result
+<p><b>templ</b>
+</p>
+!! end
+
+!! test
+Template redirect
+!! input
+{{templateredirect}}
+!! result
+<p>(test)
+</p>
+!! end
+
+!! test
+Template with argument in separate line
+!! input
+{{ templateasargtest |
+ templ = simple }}
+!! result
+<p>(test)
+</p>
+!! end
+
+!! test
+Template with complex template as argument
+!! input
+{{paramtest|
+ param ={{ templateasargtest |
+ templ = simple }}}}
+!! result
+<p>This is a test template with parameter (test)
+</p>
+!! end
+
+!! test
+Template with thumb image (with link in description)
+!! input
+{{paramtest|
+ param =[[Image:noimage.png|thumb|[[no link|link]] [[no link|caption]]]]}}
+!! result
+This is a test template with parameter <div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/index.php?title=Special:Upload&amp;wpDestFile=Noimage.png" class="new" title="File:Noimage.png">File:Noimage.png</a> <div class="thumbcaption"><a href="/index.php?title=No_link&amp;action=edit&amp;redlink=1" class="new" title="No link (page does not exist)">link</a> <a href="/index.php?title=No_link&amp;action=edit&amp;redlink=1" class="new" title="No link (page does not exist)">caption</a></div></div></div>
+
+!! end
+
+!! article
+Template:complextemplate
+!! text
+{{{1}}} {{paramtest|
+ param ={{{param}}}}}
+!! endarticle
+
+!! test
+Template with complex arguments
+!! input
+{{complextemplate|
+ param ={{ templateasargtest |
+ templ = simple }}|[[Template:complextemplate|link]]}}
+!! result
+<p><a href="/wiki/Template:Complextemplate" title="Template:Complextemplate">link</a> This is a test template with parameter (test)
+</p>
+!! end
+
+!! test
+BUG 553: link with two variables in a piped link
+!! input
+{|
+|[[{{{1}}}|{{{2}}}]]
+|}
+!! result
+<table>
+<tr>
+<td>[[{{{1}}}|{{{2}}}]]
+</td></tr></table>
+
+!! end
+
+!! test
+Magic variable as template parameter
+!! input
+{{paramtest|param={{SITENAME}}}}
+!! result
+<p>This is a test template with parameter MediaWiki
+</p>
+!! end
+
+!! article
+Template:linktest
+!! text
+[[{{{param}}}|link]]
+!! endarticle
+
+!! test
+Template parameter as link source
+!! input
+{{linktest|param=Main Page}}
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">link</a>
+</p>
+!! end
+
+
+!!article
+Template:paramtest2
+!! text
+including another template, {{paramtest|param={{{arg}}}}}
+!! endarticle
+
+!! test
+Template passing argument to another template
+!! input
+{{paramtest2|arg='hmm'}}
+!! result
+<p>including another template, This is a test template with parameter 'hmm'
+</p>
+!! end
+
+!! article
+Template:Linktest2
+!! text
+Main Page
+!! endarticle
+
+!! test
+Template as link source
+!! input
+[[{{linktest2}}]]
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">Main Page</a>
+</p>
+!! end
+
+
+!! article
+Template:loop1
+!! text
+{{loop2}}
+!! endarticle
+
+!! article
+Template:loop2
+!! text
+{{loop1}}
+!! endarticle
+
+!! test
+Template infinite loop
+!! input
+{{loop1}}
+!! result
+<p><span class="error">Template loop detected: <a href="/wiki/Template:Loop1" title="Template:Loop1">Template:Loop1</a></span>
+</p>
+!! end
+
+!! test
+Template from main namespace
+!! input
+{{:Main Page}}
+!! result
+<p>blah blah
+</p>
+!! end
+
+!! article
+Template:table
+!! text
+{|
+| 1 || 2
+|-
+| 3 || 4
+|}
+!! endarticle
+
+!! test
+BUG 529: Template with table, not included at beginning of line
+!! input
+foo {{table}}
+!! result
+<p>foo
+</p>
+<table>
+<tr>
+<td> 1 </td>
+<td> 2
+</td></tr>
+<tr>
+<td> 3 </td>
+<td> 4
+</td></tr></table>
+
+!! end
+
+!! test
+BUG 523: Template shouldn't eat newline (or add an extra one before table)
+!! input
+foo
+{{table}}
+!! result
+<p>foo
+</p>
+<table>
+<tr>
+<td> 1 </td>
+<td> 2
+</td></tr>
+<tr>
+<td> 3 </td>
+<td> 4
+</td></tr></table>
+
+!! end
+
+!! test
+BUG 41: Template parameters shown as broken links
+!! input
+{{{parameter}}}
+!! result
+<p>{{{parameter}}}
+</p>
+!! end
+
+
+!! article
+Template:MSGNW test
+!! text
+''None'' of '''this''' should be
+* interpreted
+ but rather passed unmodified
+{{test}}
+!! endarticle
+
+# hmm, fix this or just deprecate msgnw and document its behavior?
+!! test
+msgnw keyword
+!! options
+disabled
+!! input
+{{msgnw:MSGNW test}}
+!! result
+<p>''None'' of '''this''' should be
+* interpreted
+ but rather passed unmodified
+{{test}}
+</p>
+!! end
+
+!! test
+int keyword
+!! input
+{{int:youhavenewmessages|lots of money|not!}}
+!! result
+<p>You have lots of money (not!).
+</p>
+!! end
+
+!! article
+Template:Includes
+!! text
+Foo<noinclude>zar</noinclude><includeonly>bar</includeonly>
+!! endarticle
+
+!! test
+<includeonly> and <noinclude> being included
+!! input
+{{Includes}}
+!! result
+<p>Foobar
+</p>
+!! end
+
+!! article
+Template:Includes2
+!! text
+<onlyinclude>Foo</onlyinclude>bar
+!! endarticle
+
+!! test
+<onlyinclude> being included
+!! input
+{{Includes2}}
+!! result
+<p>Foo
+</p>
+!! end
+
+
+!! article
+Template:Includes3
+!! text
+<onlyinclude>Foo</onlyinclude>bar<includeonly>zar</includeonly>
+!! endarticle
+
+!! test
+<onlyinclude> and <includeonly> being included
+!! input
+{{Includes3}}
+!! result
+<p>Foo
+</p>
+!! end
+
+!! test
+<includeonly> and <noinclude> on a page
+!! input
+Foo<noinclude>zar</noinclude><includeonly>bar</includeonly>
+!! result
+<p>Foozar
+</p>
+!! end
+
+!! test
+<onlyinclude> on a page
+!! input
+<onlyinclude>Foo</onlyinclude>bar
+!! result
+<p>Foobar
+</p>
+!! end
+
+!! article
+Template:Includeonly section
+!! text
+<includeonly>
+==Includeonly section==
+</includeonly>
+==Section T-1==
+!!endarticle
+
+!! test
+Bug 6563: Edit link generation for section shown by <includeonly>
+!! input
+{{includeonly section}}
+!! result
+<h2><span class="editsection">[<a href="/index.php?title=Template:Includeonly_section&amp;action=edit&amp;section=T-1" title="Template:Includeonly section">edit</a>]</span> <span class="mw-headline" id="Includeonly_section">Includeonly section</span></h2>
+<h2><span class="editsection">[<a href="/index.php?title=Template:Includeonly_section&amp;action=edit&amp;section=T-2" title="Template:Includeonly section">edit</a>]</span> <span class="mw-headline" id="Section_T-1">Section T-1</span></h2>
+
+!! end
+
+# Uses same input as the contents of [[Template:Includeonly section]]
+!! test
+Bug 6563: Section extraction for section shown by <includeonly>
+!! options
+section=T-2
+!! input
+<includeonly>
+==Includeonly section==
+</includeonly>
+==Section T-2==
+!! result
+==Section T-2==
+!! end
+
+!! test
+Bug 6563: Edit link generation for section suppressed by <includeonly>
+!! input
+<includeonly>
+==Includeonly section==
+</includeonly>
+==Section 1==
+!! result
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a>]</span> <span class="mw-headline" id="Section_1">Section 1</span></h2>
+
+!! end
+
+!! test
+Bug 6563: Section extraction for section suppressed by <includeonly>
+!! options
+section=1
+!! input
+<includeonly>
+==Includeonly section==
+</includeonly>
+==Section 1==
+!! result
+==Section 1==
+!! end
+!! article
+Template:Top-level template
+!! text
+{{Nested template}}
+!! endarticle
+
+!! article
+Template:Nested template
+!! text
+*Item 1
+*Item 2
+!! endarticle
+
+!! test
+Line-start flag in a nested template call
+!! input
+*Item A
+*Item B
+
+{{Top-level template}}
+!! result
+<ul><li>Item A
+</li><li>Item B
+</li></ul>
+<ul><li>Item 1
+</li><li>Item 2
+</li></ul>
+
+!! end
+
+###
+### Pre-save transform tests
+###
+!! test
+pre-save transform: subst:
+!! options
+PST
+!! input
+{{subst:test}}
+!! result
+This is a test template
+!! end
+
+!! test
+pre-save transform: normal template
+!! options
+PST
+!! input
+{{test}}
+!! result
+{{test}}
+!! end
+
+!! test
+pre-save transform: nonexistent template
+!! options
+PST
+!! input
+{{thistemplatedoesnotexist}}
+!! result
+{{thistemplatedoesnotexist}}
+!! end
+
+
+!! test
+pre-save transform: subst magic variables
+!! options
+PST
+!! input
+{{subst:SITENAME}}
+!! result
+MediaWiki
+!! end
+
+# This is bug 89, which I fixed. -- wtm
+!! test
+pre-save transform: subst: templates with parameters
+!! options
+pst
+!! input
+{{subst:paramtest|param="something else"}}
+!! result
+This is a test template with parameter "something else"
+!! end
+
+!! article
+Template:nowikitest
+!! text
+<nowiki>'''not wiki'''</nowiki>
+!! endarticle
+
+!! test
+pre-save transform: nowiki in subst (bug 1188)
+!! options
+pst
+!! input
+{{subst:nowikitest}}
+!! result
+<nowiki>'''not wiki'''</nowiki>
+!! end
+
+
+!! article
+Template:commenttest
+!! text
+This template has <!-- a comment --> in it.
+!! endarticle
+
+!! test
+pre-save transform: comment in subst (bug 1936)
+!! options
+pst
+!! input
+{{subst:commenttest}}
+!! result
+This template has <!-- a comment --> in it.
+!! end
+
+!! test
+pre-save transform: unclosed tag
+!! options
+pst noxml
+!! input
+<nowiki>'''not wiki'''
+!! result
+<nowiki>'''not wiki'''
+!! end
+
+!! test
+pre-save transform: mixed tag case
+!! options
+pst noxml
+!! input
+<NOwiki>'''not wiki'''</noWIKI>
+!! result
+<NOwiki>'''not wiki'''</noWIKI>
+!! end
+
+!! test
+pre-save transform: unclosed comment in <nowiki>
+!! options
+pst noxml
+!! input
+wiki<nowiki>nowiki<!--nowiki</nowiki>wiki
+!! result
+wiki<nowiki>nowiki<!--nowiki</nowiki>wiki
+!!end
+
+!! article
+Template:dangerous
+!!text
+<span onmouseover="alert('crap')">Oh no</span>
+!!endarticle
+
+!!test
+(confirming safety of fix for subst bug 1936)
+!! input
+{{Template:dangerous}}
+!! result
+<p><span>Oh no</span>
+</p>
+!! end
+
+!! test
+pre-save transform: comment containing gallery (bug 5024)
+!! options
+pst
+!! input
+<!-- <gallery>data</gallery> -->
+!!result
+<!-- <gallery>data</gallery> -->
+!!end
+
+!! test
+pre-save transform: comment containing extension
+!! options
+pst
+!! input
+<!-- <tag>data</tag> -->
+!!result
+<!-- <tag>data</tag> -->
+!!end
+
+!! test
+pre-save transform: comment containing nowiki
+!! options
+pst
+!! input
+<!-- <nowiki>data</nowiki> -->
+!!result
+<!-- <nowiki>data</nowiki> -->
+!!end
+
+!! test
+pre-save transform: <noinclude> in subst (bug 3298)
+!! options
+pst
+!! input
+{{subst:Includes}}
+!! result
+Foobar
+!! end
+
+!! test
+pre-save transform: <onlyinclude> in subst (bug 3298)
+!! options
+pst
+!! input
+{{subst:Includes2}}
+!! result
+Foo
+!! end
+
+!! article
+Template:SubstTest
+!!text
+{{<includeonly>subst:</includeonly>Includes}}
+!! endarticle
+
+!! article
+Template:SafeSubstTest
+!! text
+{{<includeonly>safesubst:</includeonly>Includes}}
+!! endarticle
+
+!! test
+bug 22297: safesubst: works during PST
+!! options
+pst
+!! input
+{{subst:SafeSubstTest}}{{safesubst:SubstTest}}
+!! result
+FoobarFoobar
+!! end
+
+!! test
+bug 22297: safesubst: works during normal parse
+!! input
+{{SafeSubstTest}}
+!! result
+<p>Foobar
+</p>
+!! end
+
+!! test:
+subst: does not work during normal parse
+!! input
+{{SubstTest}}
+!! result
+<p>{{subst:Includes}}
+</p>
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick")
+!! options
+pst
+!! input
+[[Article (context)|]]
+[[Bar:Article|]]
+[[:Bar:Article|]]
+[[Bar:Article (context)|]]
+[[:Bar:Article (context)|]]
+[[|Article]]
+[[|Article (context)]]
+[[Bar:X (Y) Z|]]
+[[:Bar:X (Y) Z|]]
+!! result
+[[Article (context)|Article]]
+[[Bar:Article|Article]]
+[[:Bar:Article|Article]]
+[[Bar:Article (context)|Article]]
+[[:Bar:Article (context)|Article]]
+[[Article]]
+[[Article (context)]]
+[[Bar:X (Y) Z|X (Y) Z]]
+[[:Bar:X (Y) Z|X (Y) Z]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with interwiki prefix
+!! options
+pst
+!! input
+[[interwiki:Article|]]
+[[:interwiki:Article|]]
+[[interwiki:Bar:Article|]]
+[[:interwiki:Bar:Article|]]
+!! result
+[[interwiki:Article|Article]]
+[[:interwiki:Article|Article]]
+[[interwiki:Bar:Article|Bar:Article]]
+[[:interwiki:Bar:Article|Bar:Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with parens in title
+!! options
+pst title=[[Somearticle (context)]]
+!! input
+[[|Article]]
+!! result
+[[Article (context)|Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with comma in title
+!! options
+pst title=[[Someplace, Somewhere]]
+!! input
+[[|Otherplace]]
+[[Otherplace, Elsewhere|]]
+[[Otherplace, Elsewhere, Anywhere|]]
+!! result
+[[Otherplace, Somewhere|Otherplace]]
+[[Otherplace, Elsewhere|Otherplace]]
+[[Otherplace, Elsewhere, Anywhere|Otherplace]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with parens and comma
+!! options
+pst title=[[Someplace (IGNORED), Somewhere]]
+!! input
+[[|Otherplace]]
+[[Otherplace (place), Elsewhere|]]
+!! result
+[[Otherplace, Somewhere|Otherplace]]
+[[Otherplace (place), Elsewhere|Otherplace]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with comma and parens
+!! options
+pst title=[[Who, me? (context)]]
+!! input
+[[|Yes, you.]]
+[[Me, Myself, and I (1937 song)|]]
+!! result
+[[Yes, you. (context)|Yes, you.]]
+[[Me, Myself, and I (1937 song)|Me, Myself, and I]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with namespace
+!! options
+pst title=[[Ns:Somearticle]]
+!! input
+[[|Article]]
+!! result
+[[Ns:Article|Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with namespace and parens
+!! options
+pst title=[[Ns:Somearticle (context)]]
+!! input
+[[|Article]]
+!! result
+[[Ns:Article (context)|Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with namespace and comma
+!! options
+pst title=[[Ns:Somearticle, Context, Whatever]]
+!! input
+[[|Article]]
+!! result
+[[Ns:Article, Context, Whatever|Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with namespace, comma and parens
+!! options
+pst title=[[Ns:Somearticle, Context (context)]]
+!! input
+[[|Article]]
+!! result
+[[Ns:Article (context)|Article]]
+!! end
+
+!! test
+pre-save transform: context links ("pipe trick") with namespace, parens and comma
+!! options
+pst title=[[Ns:Somearticle (IGNORED), Context]]
+!! input
+[[|Article]]
+!! result
+[[Ns:Article, Context|Article]]
+!! end
+
+!! test
+pre-save transform: trim trailing empty lines
+!! options
+pst
+!! input
+Empty lines are trimmed
+
+
+
+
+!! result
+Empty lines are trimmed
+!! end
+
+!! test
+pre-save transform: Signature expansion
+!! options
+pst
+!! input
+* ~~~
+* <noinclude>~~~</noinclude>
+* <includeonly>~~~</includeonly>
+* <onlyinclude>~~~</onlyinclude>
+!! result
+* [[Special:Contributions/127.0.0.1|127.0.0.1]]
+* <noinclude>[[Special:Contributions/127.0.0.1|127.0.0.1]]</noinclude>
+* <includeonly>[[Special:Contributions/127.0.0.1|127.0.0.1]]</includeonly>
+* <onlyinclude>[[Special:Contributions/127.0.0.1|127.0.0.1]]</onlyinclude>
+!! end
+
+
+!! test
+pre-save transform: Signature expansion in nowiki tags (bug 93)
+!! options
+pst disabled
+!! input
+Shall not expand:
+
+<nowiki>~~~~</nowiki>
+
+<includeonly><nowiki>~~~~</nowiki></includeonly>
+
+<noinclude><nowiki>~~~~</nowiki></noinclude>
+
+<onlyinclude><nowiki>~~~~</nowiki></onlyinclude>
+
+{{subst:Foo}} shall be converted to FOO
+
+As well as inside noinclude/onlyinclude
+<noinclude>{{subst:Foo}}</noinclude>
+<onlyinclude>{{subst:Foo}}</onlyinclude>
+
+But not inside includeonly
+<includeonly>{{subst:Foo}}</includeonly>
+!! result
+Shall not expand:
+
+<nowiki>~~~~</nowiki>
+
+<includeonly><nowiki>~~~~</nowiki></includeonly>
+
+<noinclude><nowiki>~~~~</nowiki></noinclude>
+
+<onlyinclude><nowiki>~~~~</nowiki></onlyinclude>
+
+FOO shall be converted to FOO
+
+As well as inside noinclude/onlyinclude
+<noinclude>FOO</noinclude>
+<onlyinclude>FOO</onlyinclude>
+
+But not inside includeonly
+<includeonly>{{subst:Foo}}</includeonly>
+!! end
+
+###
+### Message transform tests
+###
+!! test
+message transform: magic variables
+!! options
+msg
+!! input
+{{SITENAME}}
+!! result
+MediaWiki
+!! end
+
+!! test
+message transform: should not transform wiki markup
+!! options
+msg
+!! input
+''test''
+!! result
+''test''
+!! end
+
+!! test
+message transform: <noinclude> in transcluded template (bug 4926)
+!! options
+msg
+!! input
+{{Includes}}
+!! result
+Foobar
+!! end
+
+!! test
+message transform: <onlyinclude> in transcluded template (bug 4926)
+!! options
+msg
+!! input
+{{Includes2}}
+!! result
+Foo
+!! end
+
+!! test
+{{#special:}} page name, known
+!! options
+msg
+!! input
+{{#special:Recentchanges}}
+!! result
+Special:RecentChanges
+!! end
+
+!! test
+{{#special:}} page name with subpage, known
+!! options
+msg
+!! input
+{{#special:Recentchanges/param}}
+!! result
+Special:RecentChanges/param
+!! end
+
+!! test
+{{#special:}} page name, unknown
+!! options
+msg
+!! input
+{{#special:foobarnonexistent}}
+!! result
+No such special page
+!! end
+
+###
+### Images
+###
+!! test
+Simple image
+!! input
+[[Image:foobar.jpg]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Right-aligned image
+!! input
+[[Image:foobar.jpg|right]]
+!! result
+<div class="floatright"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a></div>
+
+!! end
+
+!! test
+Simple image (using File: namespace, now canonical)
+!! input
+[[File:foobar.jpg]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Image with caption
+!! input
+[[Image:foobar.jpg|right|Caption text]]
+!! result
+<div class="floatright"><a href="/wiki/File:Foobar.jpg" class="image" title="Caption text"><img alt="Caption text" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a></div>
+
+!! end
+
+!! test
+Image with link parameter, wiki target
+!! input
+[[Image:foobar.jpg|link=Target page]]
+!! result
+<p><a href="/wiki/Target_page" title="Target page"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Image with link parameter, URL target
+!! input
+[[Image:foobar.jpg|link=http://example.com/]]
+!! result
+<p><a href="http://example.com/"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Image with link parameter, wgExternalLinkTarget
+!! input
+[[Image:foobar.jpg|link=http://example.com/]]
+!! config
+wgExternalLinkTarget='foobar'
+!! result
+<p><a href="http://example.com/" target="foobar"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Image with link parameter, wgExternalLinkTarget, unnamed parameter
+!! input
+[[Image:foobar.jpg|link=http://example.com/|Title]]
+!! config
+wgExternalLinkTarget='foobar'
+!! result
+<p><a href="http://example.com/" title="Title" target="foobar"><img alt="Title" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Image with empty link parameter
+!! input
+[[Image:foobar.jpg|link=]]
+!! result
+<p><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" />
+</p>
+!! end
+
+!! test
+Image with link parameter (wiki target) and unnamed parameter
+!! input
+[[Image:foobar.jpg|link=Target page|Title]]
+!! result
+<p><a href="/wiki/Target_page" title="Title"><img alt="Title" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Image with link parameter (URL target) and unnamed parameter
+!! input
+[[Image:foobar.jpg|link=http://example.com/|Title]]
+!! result
+<p><a href="http://example.com/" title="Title"><img alt="Title" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Thumbnail image with link parameter
+!! input
+[[Image:foobar.jpg|thumb|link=http://example.com/|Title]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="http://example.com/"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>Title</div></div></div>
+
+!! end
+
+!! test
+Image with frame and link
+!! input
+[[Image:Foobar.jpg|frame|left|This is a test image [[Main Page]]]]
+!! result
+<div class="thumb tleft"><div class="thumbinner" style="width:1943px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" class="thumbimage" /></a> <div class="thumbcaption">This is a test image <a href="/wiki/Main_Page" title="Main Page">Main Page</a></div></div></div>
+
+!! end
+
+!! test
+Image with frame and link and explicit alt
+!! input
+[[Image:Foobar.jpg|frame|left|This is a test image [[Main Page]]|alt=Altitude]]
+!! result
+<div class="thumb tleft"><div class="thumbinner" style="width:1943px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Altitude" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" class="thumbimage" /></a> <div class="thumbcaption">This is a test image <a href="/wiki/Main_Page" title="Main Page">Main Page</a></div></div></div>
+
+!! end
+
+!! test
+Image with wiki markup in implicit alt
+!! input
+[[Image:Foobar.jpg|testing '''bold''' in alt]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image" title="testing bold in alt"><img alt="testing bold in alt" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Image with wiki markup in explicit alt
+!! input
+[[Image:Foobar.jpg|alt=testing '''bold''' in alt]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="testing bold in alt" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Link to image page- image page normally doesn't exists, hence edit link
+Add test with existing image page
+#<p><a href="/wiki/File:Test" title="Image:Test">Image:test</a>
+!! input
+[[:Image:test]]
+!! result
+<p><a href="/index.php?title=File:Test&amp;action=edit&amp;redlink=1" class="new" title="File:Test (page does not exist)">Image:test</a>
+</p>
+!! end
+
+!! test
+bug 18784 Link to non-existent image page with caption should use caption as link text
+!! input
+[[:Image:test|caption]]
+!! result
+<p><a href="/index.php?title=File:Test&amp;action=edit&amp;redlink=1" class="new" title="File:Test (page does not exist)">caption</a>
+</p>
+!! end
+
+!! test
+Frameless image caption with a free URL
+!! input
+[[Image:foobar.jpg|http://example.com]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image" title="http://example.com"><img alt="http://example.com" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Thumbnail image caption with a free URL
+!! input
+[[Image:foobar.jpg|thumb|http://example.com]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div><a rel="nofollow" class="external free" href="http://example.com">http://example.com</a></div></div></div>
+
+!! end
+
+!! test
+Thumbnail image caption with a free URL and explicit alt
+!! input
+[[Image:foobar.jpg|thumb|http://example.com|alt=Alteration]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Alteration" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div><a rel="nofollow" class="external free" href="http://example.com">http://example.com</a></div></div></div>
+
+!! end
+
+!! test
+BUG 1887: A ISBN with a thumbnail
+!! input
+[[Image:foobar.jpg|thumb|ISBN 1235467890]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div><a href="/wiki/Special:BookSources/1235467890" class="internal mw-magiclink-isbn">ISBN 1235467890</a></div></div></div>
+
+!! end
+
+!! test
+BUG 1887: A RFC with a thumbnail
+!! input
+[[Image:foobar.jpg|thumb|This is RFC 12354]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>This is <a class="external mw-magiclink-rfc" href="//tools.ietf.org/html/rfc12354">RFC 12354</a></div></div></div>
+
+!! end
+
+!! test
+BUG 1887: A mailto link with a thumbnail
+!! input
+[[Image:foobar.jpg|thumb|Please mailto:nobody@example.com]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>Please <a rel="nofollow" class="external free" href="mailto:nobody@example.com">mailto:nobody@example.com</a></div></div></div>
+
+!! end
+
+# Pending resolution to bug 368
+!! test
+BUG 648: Frameless image caption with a link
+!! input
+[[Image:foobar.jpg|text with a [[link]] in it]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image" title="text with a link in it"><img alt="text with a link in it" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+BUG 648: Frameless image caption with a link (suffix)
+!! input
+[[Image:foobar.jpg|text with a [[link]]foo in it]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image" title="text with a linkfoo in it"><img alt="text with a linkfoo in it" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+BUG 648: Frameless image caption with an interwiki link
+!! input
+[[Image:foobar.jpg|text with a [[MeatBall:Link]] in it]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image" title="text with a MeatBall:Link in it"><img alt="text with a MeatBall:Link in it" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+BUG 648: Frameless image caption with a piped interwiki link
+!! input
+[[Image:foobar.jpg|text with a [[MeatBall:Link|link]] in it]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image" title="text with a link in it"><img alt="text with a link in it" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Escape HTML special chars in image alt text
+!! input
+[[Image:foobar.jpg|& < > "]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image" title="&amp; &lt; &gt; &quot;"><img alt="&amp; &lt; &gt; &quot;" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+BUG 499: Alt text should have &#1234;, not &amp;1234;
+!! input
+[[Image:foobar.jpg|&#9792;]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image" title="♀"><img alt="♀" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!! end
+
+!! test
+Broken image caption with link
+!! input
+[[Image:Foobar.jpg|thumb|This is a broken caption. But [[Main Page|this]] is just an ordinary link.
+!! result
+<p>[[Image:Foobar.jpg|thumb|This is a broken caption. But <a href="/wiki/Main_Page" title="Main Page">this</a> is just an ordinary link.
+</p>
+!! end
+
+!! test
+Image caption containing another image
+!! input
+[[Image:Foobar.jpg|thumb|This is a caption with another [[Image:icon.png|image]] inside it!]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>This is a caption with another <a href="/index.php?title=Special:Upload&amp;wpDestFile=Icon.png" class="new" title="File:Icon.png">image</a> inside it!</div></div></div>
+
+!! end
+
+!! test
+Image caption containing a newline
+!! input
+[[Image:Foobar.jpg|This
+*is some text]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image" title="This *is some text"><img alt="This *is some text" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!!end
+
+
+!! test
+Bug 3090: External links other than http: in image captions
+!! input
+[[Image:Foobar.jpg|thumb|200px|This caption has [irc://example.net irc] and [https://example.com Secure] ext links in it.]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:202px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="200" height="23" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>This caption has <a rel="nofollow" class="external text" href="irc://example.net">irc</a> and <a rel="nofollow" class="external text" href="https://example.com">Secure</a> ext links in it.</div></div></div>
+
+!! end
+
+!! article
+File:Barfoo.jpg
+!! text
+#REDIRECT [[File:Barfoo.jpg]]
+!! endarticle
+
+!! test
+Redirected image
+!! input
+[[Image:Barfoo.jpg]]
+!! result
+<p><a href="/wiki/File:Barfoo.jpg" title="File:Barfoo.jpg">File:Barfoo.jpg</a>
+</p>
+!! end
+
+!! test
+Missing image with uploads disabled
+!! options
+wgEnableUploads=0
+!! input
+[[Image:Foobaz.jpg]]
+!! result
+<p><a href="/wiki/File:Foobaz.jpg" title="File:Foobaz.jpg">File:Foobaz.jpg</a>
+</p>
+!! end
+
+
+###
+### Subpages
+###
+!! article
+Subpage test/subpage
+!! text
+foo
+!! endarticle
+
+!! test
+Subpage link
+!! options
+subpage title=[[Subpage test]]
+!! input
+[[/subpage]]
+!! result
+<p><a href="/wiki/Subpage_test/subpage" title="Subpage test/subpage">/subpage</a>
+</p>
+!! end
+
+!! test
+Subpage noslash link
+!! options
+subpage title=[[Subpage test]]
+!!input
+[[/subpage/]]
+!! result
+<p><a href="/wiki/Subpage_test/subpage" title="Subpage test/subpage">subpage</a>
+</p>
+!! end
+
+!! test
+Disabled subpages
+!! input
+[[/subpage]]
+!! result
+<p><a href="/index.php?title=/subpage&amp;action=edit&amp;redlink=1" class="new" title="/subpage (page does not exist)">/subpage</a>
+</p>
+!! end
+
+!! test
+BUG 561: {{/Subpage}}
+!! options
+subpage title=[[Page]]
+!! input
+{{/Subpage}}
+!! result
+<p><a href="/index.php?title=Page/Subpage&amp;action=edit&amp;redlink=1" class="new" title="Page/Subpage (page does not exist)">Page/Subpage</a>
+</p>
+!! end
+
+###
+### Categories
+###
+!! article
+Category:MediaWiki User's Guide
+!! text
+blah
+!! endarticle
+
+!! test
+Link to category
+!! input
+[[:Category:MediaWiki User's Guide]]
+!! result
+<p><a href="/wiki/Category:MediaWiki_User%27s_Guide" title="Category:MediaWiki User's Guide">Category:MediaWiki User's Guide</a>
+</p>
+!! end
+
+!! test
+Simple category
+!! options
+cat
+!! input
+[[Category:MediaWiki User's Guide]]
+!! result
+<a href="/wiki/Category:MediaWiki_User%27s_Guide" title="Category:MediaWiki User's Guide">MediaWiki User's Guide</a>
+!! end
+
+!! test
+PAGESINCATEGORY invalid title fatal (r33546 fix)
+!! input
+{{PAGESINCATEGORY:<bogus>}}
+!! result
+<p>0
+</p>
+!! end
+
+###
+### Inter-language links
+###
+!! test
+Inter-language links
+!! options
+ill
+!! input
+[[es:Alimento]]
+[[fr:Nourriture]]
+[[zh:&#39135;&#21697;]]
+!! result
+es:Alimento fr:Nourriture zh:食å“
+!! end
+
+###
+### Sections
+###
+!! test
+Basic section headings
+!! input
+== Headline 1 ==
+Some text
+
+==Headline 2==
+More
+===Smaller headline===
+Blah blah
+!! result
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Headline 1">edit</a>]</span> <span class="mw-headline" id="Headline_1"> Headline 1 </span></h2>
+<p>Some text
+</p>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Headline 2">edit</a>]</span> <span class="mw-headline" id="Headline_2">Headline 2</span></h2>
+<p>More
+</p>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: Smaller headline">edit</a>]</span> <span class="mw-headline" id="Smaller_headline">Smaller headline</span></h3>
+<p>Blah blah
+</p>
+!! end
+
+!! test
+Section headings with TOC
+!! input
+== Headline 1 ==
+=== Subheadline 1 ===
+===== Skipping a level =====
+====== Skipping a level ======
+
+== Headline 2 ==
+Some text
+===Another headline===
+!! result
+<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#Headline_1"><span class="tocnumber">1</span> <span class="toctext">Headline 1</span></a>
+<ul>
+<li class="toclevel-2 tocsection-2"><a href="#Subheadline_1"><span class="tocnumber">1.1</span> <span class="toctext">Subheadline 1</span></a>
+<ul>
+<li class="toclevel-3 tocsection-3"><a href="#Skipping_a_level"><span class="tocnumber">1.1.1</span> <span class="toctext">Skipping a level</span></a>
+<ul>
+<li class="toclevel-4 tocsection-4"><a href="#Skipping_a_level_2"><span class="tocnumber">1.1.1.1</span> <span class="toctext">Skipping a level</span></a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-5"><a href="#Headline_2"><span class="tocnumber">2</span> <span class="toctext">Headline 2</span></a>
+<ul>
+<li class="toclevel-2 tocsection-6"><a href="#Another_headline"><span class="tocnumber">2.1</span> <span class="toctext">Another headline</span></a></li>
+</ul>
+</li>
+</ul>
+</td></tr></table>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Headline 1">edit</a>]</span> <span class="mw-headline" id="Headline_1"> Headline 1 </span></h2>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Subheadline 1">edit</a>]</span> <span class="mw-headline" id="Subheadline_1"> Subheadline 1 </span></h3>
+<h5><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: Skipping a level">edit</a>]</span> <span class="mw-headline" id="Skipping_a_level"> Skipping a level </span></h5>
+<h6><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=4" title="Edit section: Skipping a level">edit</a>]</span> <span class="mw-headline" id="Skipping_a_level_2"> Skipping a level </span></h6>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=5" title="Edit section: Headline 2">edit</a>]</span> <span class="mw-headline" id="Headline_2"> Headline 2 </span></h2>
+<p>Some text
+</p>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=6" title="Edit section: Another headline">edit</a>]</span> <span class="mw-headline" id="Another_headline">Another headline</span></h3>
+
+!! end
+
+# perl -e 'print "="x$_," Level $_ heading","="x$_,"\n" for 1..10'
+!! test
+Handling of sections up to level 6 and beyond
+!! input
+= Level 1 Heading=
+== Level 2 Heading==
+=== Level 3 Heading===
+==== Level 4 Heading====
+===== Level 5 Heading=====
+====== Level 6 Heading======
+======= Level 7 Heading=======
+======== Level 8 Heading========
+========= Level 9 Heading=========
+========== Level 10 Heading==========
+!! result
+<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#Level_1_Heading"><span class="tocnumber">1</span> <span class="toctext">Level 1 Heading</span></a>
+<ul>
+<li class="toclevel-2 tocsection-2"><a href="#Level_2_Heading"><span class="tocnumber">1.1</span> <span class="toctext">Level 2 Heading</span></a>
+<ul>
+<li class="toclevel-3 tocsection-3"><a href="#Level_3_Heading"><span class="tocnumber">1.1.1</span> <span class="toctext">Level 3 Heading</span></a>
+<ul>
+<li class="toclevel-4 tocsection-4"><a href="#Level_4_Heading"><span class="tocnumber">1.1.1.1</span> <span class="toctext">Level 4 Heading</span></a>
+<ul>
+<li class="toclevel-5 tocsection-5"><a href="#Level_5_Heading"><span class="tocnumber">1.1.1.1.1</span> <span class="toctext">Level 5 Heading</span></a>
+<ul>
+<li class="toclevel-6 tocsection-6"><a href="#Level_6_Heading"><span class="tocnumber">1.1.1.1.1.1</span> <span class="toctext">Level 6 Heading</span></a></li>
+<li class="toclevel-6 tocsection-7"><a href="#.3D_Level_7_Heading.3D"><span class="tocnumber">1.1.1.1.1.2</span> <span class="toctext">= Level 7 Heading=</span></a></li>
+<li class="toclevel-6 tocsection-8"><a href="#.3D.3D_Level_8_Heading.3D.3D"><span class="tocnumber">1.1.1.1.1.3</span> <span class="toctext">== Level 8 Heading==</span></a></li>
+<li class="toclevel-6 tocsection-9"><a href="#.3D.3D.3D_Level_9_Heading.3D.3D.3D"><span class="tocnumber">1.1.1.1.1.4</span> <span class="toctext">=== Level 9 Heading===</span></a></li>
+<li class="toclevel-6 tocsection-10"><a href="#.3D.3D.3D.3D_Level_10_Heading.3D.3D.3D.3D"><span class="tocnumber">1.1.1.1.1.5</span> <span class="toctext">==== Level 10 Heading====</span></a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+</td></tr></table>
+<h1><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Level 1 Heading">edit</a>]</span> <span class="mw-headline" id="Level_1_Heading"> Level 1 Heading</span></h1>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Level 2 Heading">edit</a>]</span> <span class="mw-headline" id="Level_2_Heading"> Level 2 Heading</span></h2>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: Level 3 Heading">edit</a>]</span> <span class="mw-headline" id="Level_3_Heading"> Level 3 Heading</span></h3>
+<h4><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=4" title="Edit section: Level 4 Heading">edit</a>]</span> <span class="mw-headline" id="Level_4_Heading"> Level 4 Heading</span></h4>
+<h5><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=5" title="Edit section: Level 5 Heading">edit</a>]</span> <span class="mw-headline" id="Level_5_Heading"> Level 5 Heading</span></h5>
+<h6><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=6" title="Edit section: Level 6 Heading">edit</a>]</span> <span class="mw-headline" id="Level_6_Heading"> Level 6 Heading</span></h6>
+<h6><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=7" title="Edit section: = Level 7 Heading=">edit</a>]</span> <span class="mw-headline" id=".3D_Level_7_Heading.3D">= Level 7 Heading=</span></h6>
+<h6><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=8" title="Edit section: == Level 8 Heading==">edit</a>]</span> <span class="mw-headline" id=".3D.3D_Level_8_Heading.3D.3D">== Level 8 Heading==</span></h6>
+<h6><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=9" title="Edit section: === Level 9 Heading===">edit</a>]</span> <span class="mw-headline" id=".3D.3D.3D_Level_9_Heading.3D.3D.3D">=== Level 9 Heading===</span></h6>
+<h6><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=10" title="Edit section: ==== Level 10 Heading====">edit</a>]</span> <span class="mw-headline" id=".3D.3D.3D.3D_Level_10_Heading.3D.3D.3D.3D">==== Level 10 Heading====</span></h6>
+
+!! end
+
+!! test
+TOC regression (bug 9764)
+!! input
+== title 1 ==
+=== title 1.1 ===
+==== title 1.1.1 ====
+=== title 1.2 ===
+== title 2 ==
+=== title 2.1 ===
+!! result
+<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#title_1"><span class="tocnumber">1</span> <span class="toctext">title 1</span></a>
+<ul>
+<li class="toclevel-2 tocsection-2"><a href="#title_1.1"><span class="tocnumber">1.1</span> <span class="toctext">title 1.1</span></a>
+<ul>
+<li class="toclevel-3 tocsection-3"><a href="#title_1.1.1"><span class="tocnumber">1.1.1</span> <span class="toctext">title 1.1.1</span></a></li>
+</ul>
+</li>
+<li class="toclevel-2 tocsection-4"><a href="#title_1.2"><span class="tocnumber">1.2</span> <span class="toctext">title 1.2</span></a></li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-5"><a href="#title_2"><span class="tocnumber">2</span> <span class="toctext">title 2</span></a>
+<ul>
+<li class="toclevel-2 tocsection-6"><a href="#title_2.1"><span class="tocnumber">2.1</span> <span class="toctext">title 2.1</span></a></li>
+</ul>
+</li>
+</ul>
+</td></tr></table>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: title 1">edit</a>]</span> <span class="mw-headline" id="title_1"> title 1 </span></h2>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: title 1.1">edit</a>]</span> <span class="mw-headline" id="title_1.1"> title 1.1 </span></h3>
+<h4><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: title 1.1.1">edit</a>]</span> <span class="mw-headline" id="title_1.1.1"> title 1.1.1 </span></h4>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=4" title="Edit section: title 1.2">edit</a>]</span> <span class="mw-headline" id="title_1.2"> title 1.2 </span></h3>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=5" title="Edit section: title 2">edit</a>]</span> <span class="mw-headline" id="title_2"> title 2 </span></h2>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=6" title="Edit section: title 2.1">edit</a>]</span> <span class="mw-headline" id="title_2.1"> title 2.1 </span></h3>
+
+!! end
+
+!! test
+TOC with wgMaxTocLevel=3 (bug 6204)
+!! options
+wgMaxTocLevel=3
+!! input
+== title 1 ==
+=== title 1.1 ===
+==== title 1.1.1 ====
+=== title 1.2 ===
+== title 2 ==
+=== title 2.1 ===
+!! result
+<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#title_1"><span class="tocnumber">1</span> <span class="toctext">title 1</span></a>
+<ul>
+<li class="toclevel-2 tocsection-2"><a href="#title_1.1"><span class="tocnumber">1.1</span> <span class="toctext">title 1.1</span></a></li>
+<li class="toclevel-2 tocsection-4"><a href="#title_1.2"><span class="tocnumber">1.2</span> <span class="toctext">title 1.2</span></a></li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-5"><a href="#title_2"><span class="tocnumber">2</span> <span class="toctext">title 2</span></a>
+<ul>
+<li class="toclevel-2 tocsection-6"><a href="#title_2.1"><span class="tocnumber">2.1</span> <span class="toctext">title 2.1</span></a></li>
+</ul>
+</li>
+</ul>
+</td></tr></table>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: title 1">edit</a>]</span> <span class="mw-headline" id="title_1"> title 1 </span></h2>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: title 1.1">edit</a>]</span> <span class="mw-headline" id="title_1.1"> title 1.1 </span></h3>
+<h4><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: title 1.1.1">edit</a>]</span> <span class="mw-headline" id="title_1.1.1"> title 1.1.1 </span></h4>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=4" title="Edit section: title 1.2">edit</a>]</span> <span class="mw-headline" id="title_1.2"> title 1.2 </span></h3>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=5" title="Edit section: title 2">edit</a>]</span> <span class="mw-headline" id="title_2"> title 2 </span></h2>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=6" title="Edit section: title 2.1">edit</a>]</span> <span class="mw-headline" id="title_2.1"> title 2.1 </span></h3>
+
+!! end
+
+!! test
+TOC with wgMaxTocLevel=3 and two level four headings (bug 6204)
+!! options
+wgMaxTocLevel=3
+!! input
+==Section 1==
+===Section 1.1===
+====Section 1.1.1====
+====Section 1.1.1.1====
+==Section 2==
+!! result
+<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a>
+<ul>
+<li class="toclevel-2 tocsection-2"><a href="#Section_1.1"><span class="tocnumber">1.1</span> <span class="toctext">Section 1.1</span></a></li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-5"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a></li>
+</ul>
+</td></tr></table>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Section 1">edit</a>]</span> <span class="mw-headline" id="Section_1">Section 1</span></h2>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Section 1.1">edit</a>]</span> <span class="mw-headline" id="Section_1.1">Section 1.1</span></h3>
+<h4><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: Section 1.1.1">edit</a>]</span> <span class="mw-headline" id="Section_1.1.1">Section 1.1.1</span></h4>
+<h4><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=4" title="Edit section: Section 1.1.1.1">edit</a>]</span> <span class="mw-headline" id="Section_1.1.1.1">Section 1.1.1.1</span></h4>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=5" title="Edit section: Section 2">edit</a>]</span> <span class="mw-headline" id="Section_2">Section 2</span></h2>
+
+!! end
+
+
+!! test
+Resolving duplicate section names
+!! input
+== Foo bar ==
+== Foo bar ==
+!! result
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Foo bar">edit</a>]</span> <span class="mw-headline" id="Foo_bar"> Foo bar </span></h2>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Foo bar">edit</a>]</span> <span class="mw-headline" id="Foo_bar_2"> Foo bar </span></h2>
+
+!! end
+
+!! test
+Resolving duplicate section names with differing case (bug 10721)
+!! input
+== Foo bar ==
+== Foo Bar ==
+!! result
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Foo bar">edit</a>]</span> <span class="mw-headline" id="Foo_bar"> Foo bar </span></h2>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Foo Bar">edit</a>]</span> <span class="mw-headline" id="Foo_Bar_2"> Foo Bar </span></h2>
+
+!! end
+
+!! article
+Template:sections
+!! text
+===Section 1===
+==Section 2==
+!! endarticle
+
+!! test
+Template with sections, __NOTOC__
+!! input
+__NOTOC__
+==Section 0==
+{{sections}}
+==Section 4==
+!! result
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Section 0">edit</a>]</span> <span class="mw-headline" id="Section_0">Section 0</span></h2>
+<h3><span class="editsection">[<a href="/index.php?title=Template:Sections&amp;action=edit&amp;section=T-1" title="Template:Sections">edit</a>]</span> <span class="mw-headline" id="Section_1">Section 1</span></h3>
+<h2><span class="editsection">[<a href="/index.php?title=Template:Sections&amp;action=edit&amp;section=T-2" title="Template:Sections">edit</a>]</span> <span class="mw-headline" id="Section_2">Section 2</span></h2>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: Section 4">edit</a>]</span> <span class="mw-headline" id="Section_4">Section 4</span></h2>
+
+!! end
+
+!! test
+__NOEDITSECTION__ keyword
+!! input
+__NOEDITSECTION__
+==Section 1==
+==Section 2==
+!! result
+<h2> <span class="mw-headline" id="Section_1">Section 1</span></h2>
+<h2> <span class="mw-headline" id="Section_2">Section 2</span></h2>
+
+!! end
+
+!! test
+Link inside a section heading
+!! input
+==Section with a [[Main Page|link]] in it==
+!! result
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: Section with a link in it">edit</a>]</span> <span class="mw-headline" id="Section_with_a_link_in_it">Section with a <a href="/wiki/Main_Page" title="Main Page">link</a> in it</span></h2>
+
+!! end
+
+!! test
+TOC regression (bug 12077)
+!! input
+__TOC__
+== title 1 ==
+=== title 1.1 ===
+== title 2 ==
+!! result
+<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#title_1"><span class="tocnumber">1</span> <span class="toctext">title 1</span></a>
+<ul>
+<li class="toclevel-2 tocsection-2"><a href="#title_1.1"><span class="tocnumber">1.1</span> <span class="toctext">title 1.1</span></a></li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-3"><a href="#title_2"><span class="tocnumber">2</span> <span class="toctext">title 2</span></a></li>
+</ul>
+</td></tr></table>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: title 1">edit</a>]</span> <span class="mw-headline" id="title_1"> title 1 </span></h2>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: title 1.1">edit</a>]</span> <span class="mw-headline" id="title_1.1"> title 1.1 </span></h3>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: title 2">edit</a>]</span> <span class="mw-headline" id="title_2"> title 2 </span></h2>
+
+!! end
+
+!! test
+BUG 1219 URL next to image (good)
+!! input
+http://example.com [[Image:foobar.jpg]]
+!! result
+<p><a rel="nofollow" class="external free" href="http://example.com">http://example.com</a> <a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!!end
+
+!! test
+Short headings with trailing space should match behaviour of Parser::doHeadings (bug 19910)
+!! input
+===
+The line above must have a trailing space!
+=== <!--
+--> <!-- -->
+But just in case it doesn't...
+!! result
+<h1><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: =">edit</a>]</span> <span class="mw-headline" id=".3D">=</span></h1>
+<p>The line above must have a trailing space!
+</p>
+<h1><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: =">edit</a>]</span> <span class="mw-headline" id=".3D_2">=</span></h1>
+<p>But just in case it doesn't...
+</p>
+!! end
+
+!! test
+Header with special characters (bug 25462)
+!! input
+The tooltips shall not show entities to the user (ie. be double escaped)
+
+== text > text ==
+section 1
+
+== text < text ==
+section 2
+
+== text & text ==
+section 3
+
+== text ' text ==
+section 4
+
+== text " text ==
+section 5
+!! result
+<p>The tooltips shall not show entities to the user (ie. be double escaped)
+</p>
+<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#text_.3E_text"><span class="tocnumber">1</span> <span class="toctext">text &gt; text</span></a></li>
+<li class="toclevel-1 tocsection-2"><a href="#text_.3C_text"><span class="tocnumber">2</span> <span class="toctext">text &lt; text</span></a></li>
+<li class="toclevel-1 tocsection-3"><a href="#text_.26_text"><span class="tocnumber">3</span> <span class="toctext">text &amp; text</span></a></li>
+<li class="toclevel-1 tocsection-4"><a href="#text_.27_text"><span class="tocnumber">4</span> <span class="toctext">text ' text</span></a></li>
+<li class="toclevel-1 tocsection-5"><a href="#text_.22_text"><span class="tocnumber">5</span> <span class="toctext">text " text</span></a></li>
+</ul>
+</td></tr></table>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: text > text">edit</a>]</span> <span class="mw-headline" id="text_.3E_text"> text &gt; text </span></h2>
+<p>section 1
+</p>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: text &lt; text">edit</a>]</span> <span class="mw-headline" id="text_.3C_text"> text &lt; text </span></h2>
+<p>section 2
+</p>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: text &amp; text">edit</a>]</span> <span class="mw-headline" id="text_.26_text"> text &amp; text </span></h2>
+<p>section 3
+</p>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=4" title="Edit section: text ' text">edit</a>]</span> <span class="mw-headline" id="text_.27_text"> text ' text </span></h2>
+<p>section 4
+</p>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=5" title="Edit section: text &quot; text">edit</a>]</span> <span class="mw-headline" id="text_.22_text"> text " text </span></h2>
+<p>section 5
+</p>
+!! end
+
+!! test
+BUG 1219 URL next to image (broken)
+!! input
+http://example.com[[Image:foobar.jpg]]
+!! result
+<p><a rel="nofollow" class="external free" href="http://example.com">http://example.com</a><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
+</p>
+!!end
+
+!! test
+Bug 1186 news: in the middle of text
+!! input
+http://en.wikinews.org/wiki/Wikinews:Workplace
+!! result
+<p><a rel="nofollow" class="external free" href="http://en.wikinews.org/wiki/Wikinews:Workplace">http://en.wikinews.org/wiki/Wikinews:Workplace</a>
+</p>
+!!end
+
+
+!! test
+Namespaced link must have a title
+!! input
+[[Project:]]
+!! result
+<p>[[Project:]]
+</p>
+!!end
+
+!! test
+Namespaced link must have a title (bad fragment version)
+!! input
+[[Project:#fragment]]
+!! result
+<p>[[Project:#fragment]]
+</p>
+!!end
+
+
+!! test
+div with no attributes
+!! input
+<div>HTML rocks</div>
+!! result
+<div>HTML rocks</div>
+
+!! end
+
+!! test
+div with double-quoted attribute
+!! input
+<div id="rock">HTML rocks</div>
+!! result
+<div id="rock">HTML rocks</div>
+
+!! end
+
+!! test
+div with single-quoted attribute
+!! input
+<div id='rock'>HTML rocks</div>
+!! result
+<div id="rock">HTML rocks</div>
+
+!! end
+
+!! test
+div with unquoted attribute
+!! input
+<div id=rock>HTML rocks</div>
+!! result
+<div id="rock">HTML rocks</div>
+
+!! end
+
+!! test
+div with illegal double attributes
+!! input
+<div align="center" align="right">HTML rocks</div>
+!! result
+<div align="right">HTML rocks</div>
+
+!!end
+
+!! test
+HTML multiple attributes correction
+!! input
+<p class="error" class="awesome">Awesome!</p>
+!! result
+<p class="awesome">Awesome!</p>
+
+!!end
+
+!! test
+Table multiple attributes correction
+!! input
+{|
+!+ class="error" class="awesome"| status
+|}
+!! result
+<table>
+<tr>
+<th class="awesome"> status
+</th></tr></table>
+
+!!end
+
+!! test
+DIV IN UPPERCASE
+!! input
+<DIV ALIGN="center">HTML ROCKS</DIV>
+!! result
+<div align="center">HTML ROCKS</div>
+
+!!end
+
+
+!! test
+text with amp in the middle of nowhere
+!! input
+Remember AT&T?
+!!result
+<p>Remember AT&amp;T?
+</p>
+!! end
+
+!! test
+text with character entity: eacute
+!! input
+I always thought &eacute; was a cute letter.
+!! result
+<p>I always thought &#233; was a cute letter.
+</p>
+!! end
+
+!! test
+text with undefined character entity: xacute
+!! input
+I always thought &xacute; was a cute letter.
+!! result
+<p>I always thought &amp;xacute; was a cute letter.
+</p>
+!! end
+
+
+###
+### Media links
+###
+
+!! test
+Media link
+!! input
+[[Media:Foobar.jpg]]
+!! result
+<p><a href="http://example.com/images/3/3a/Foobar.jpg" class="internal" title="Foobar.jpg">Media:Foobar.jpg</a>
+</p>
+!! end
+
+!! test
+Media link with text
+!! input
+[[Media:Foobar.jpg|A neat file to look at]]
+!! result
+<p><a href="http://example.com/images/3/3a/Foobar.jpg" class="internal" title="Foobar.jpg">A neat file to look at</a>
+</p>
+!! end
+
+# FIXME: this is still bad HTML tag nesting
+!! test
+Media link with nasty text
+fixme: doBlockLevels won't wrap this in a paragraph because it contains a div
+!! input
+[[Media:Foobar.jpg|Safe Link<div style=display:none>" onmouseover="alert(document.cookie)" onfoo="</div>]]
+!! result
+<a href="http://example.com/images/3/3a/Foobar.jpg" class="internal" title="Foobar.jpg">Safe Link&lt;div style="display:none"&gt;" onmouseover="alert(document.cookie)" onfoo="&lt;/div&gt;</a>
+
+!! end
+
+!! test
+Media link to nonexistent file (bug 1702)
+!! input
+[[Media:No such.jpg]]
+!! result
+<p><a href="/index.php?title=Special:Upload&amp;wpDestFile=No_such.jpg" class="new" title="No such.jpg">Media:No such.jpg</a>
+</p>
+!! end
+
+!! test
+Image link to nonexistent file (bug 1850 - good)
+!! input
+[[Image:No such.jpg]]
+!! result
+<p><a href="/index.php?title=Special:Upload&amp;wpDestFile=No_such.jpg" class="new" title="File:No such.jpg">File:No such.jpg</a>
+</p>
+!! end
+
+!! test
+:Image link to nonexistent file (bug 1850 - bad)
+!! input
+[[:Image:No such.jpg]]
+!! result
+<p><a href="/index.php?title=File:No_such.jpg&amp;action=edit&amp;redlink=1" class="new" title="File:No such.jpg (page does not exist)">Image:No such.jpg</a>
+</p>
+!! end
+
+
+
+!! test
+Character reference normalization in link text (bug 1938)
+!! input
+[[Main Page|this&that]]
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">this&amp;that</a>
+</p>
+!!end
+
+!! article
+×Ö·
+!! text
+Test for unicode normalization
+
+The page's name is U+05d0 U+05b7, with non-canonical form U+FB2E
+!! endarticle
+
+!! test
+(bug 19451) Links should refer to the normalized form.
+!! input
+[[&#xFB2E;]]
+[[&#x5d0;&#x5b7;]]
+[[&#x5d0;Ö·]]
+[[×&#x5b7;]]
+[[×Ö·]]
+!! result
+<p><a href="/wiki/%D7%90%D6%B7" title="×Ö·">&#xfb2e;</a>
+<a href="/wiki/%D7%90%D6%B7" title="×Ö·">&#x5d0;&#x5b7;</a>
+<a href="/wiki/%D7%90%D6%B7" title="×Ö·">&#x5d0;Ö·</a>
+<a href="/wiki/%D7%90%D6%B7" title="×Ö·">×&#x5b7;</a>
+<a href="/wiki/%D7%90%D6%B7" title="×Ö·">×Ö·</a>
+</p>
+!! end
+
+!! test
+Empty attribute crash test (bug 2067)
+!! input
+<font color="">foo</font>
+!! result
+<p><font color="">foo</font>
+</p>
+!! end
+
+!! test
+Empty attribute crash test single-quotes (bug 2067)
+!! input
+<font color=''>foo</font>
+!! result
+<p><font color="">foo</font>
+</p>
+!! end
+
+!! test
+Attribute test: equals, then nothing
+!! input
+<font color=>foo</font>
+!! result
+<p><font>foo</font>
+</p>
+!! end
+
+!! test
+Attribute test: unquoted value
+!! input
+<font color=x>foo</font>
+!! result
+<p><font color="x">foo</font>
+</p>
+!! end
+
+!! test
+Attribute test: unquoted but illegal value (hash)
+!! input
+<font color=#x>foo</font>
+!! result
+<p><font color="#x">foo</font>
+</p>
+!! end
+
+!! test
+Attribute test: no value
+!! input
+<font color>foo</font>
+!! result
+<p><font color="color">foo</font>
+</p>
+!! end
+
+!! test
+Bug 2095: link with three closing brackets
+!! input
+[[Main Page]]]
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">Main Page</a>]
+</p>
+!! end
+
+!! test
+Bug 2095: link with pipe and three closing brackets
+!! input
+[[Main Page|link]]]
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">link</a>]
+</p>
+!! end
+
+!! test
+Bug 2095: link with pipe and three closing brackets, version 2
+!! input
+[[Main Page|[http://example.com/]]]
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">[http://example.com/]</a>
+</p>
+!! end
+
+
+###
+### Safety
+###
+
+!! article
+Template:Dangerous attribute
+!! text
+" onmouseover="alert(document.cookie)
+!! endarticle
+
+!! article
+Template:Dangerous style attribute
+!! text
+border-size: expression(alert(document.cookie))
+!! endarticle
+
+!! article
+Template:Div style
+!! text
+<div style="float: right; {{{1}}}">Magic div</div>
+!! endarticle
+
+!! test
+Bug 2304: HTML attribute safety (safe template; regression bug 2309)
+!! input
+<div title="{{test}}"></div>
+!! result
+<div title="This is a test template"></div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (dangerous template; 2309)
+!! input
+<div title="{{dangerous attribute}}"></div>
+!! result
+<div title=""></div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (dangerous style template; 2309)
+!! input
+<div style="{{dangerous style attribute}}"></div>
+!! result
+<div style="/* insecure input */"></div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (safe parameter; 2309)
+!! input
+{{div style|width: 200px}}
+!! result
+<div style="float: right; width: 200px">Magic div</div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (unsafe parameter; 2309)
+!! input
+{{div style|width: expression(alert(document.cookie))}}
+!! result
+<div style="/* insecure input */">Magic div</div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (unsafe breakout parameter; 2309)
+!! input
+{{div style|"><script>alert(document.cookie)</script>}}
+!! result
+<div style="float: right;">&lt;script&gt;alert(document.cookie)&lt;/script&gt;"&gt;Magic div</div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (unsafe breakout parameter 2; 2309)
+!! input
+{{div style|" ><script>alert(document.cookie)</script>}}
+!! result
+<div style="float: right;">&lt;script&gt;alert(document.cookie)&lt;/script&gt;"&gt;Magic div</div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (link)
+!! input
+<div title="[[Main Page]]"></div>
+!! result
+<div title="&#91;&#91;Main Page]]"></div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (italics)
+!! input
+<div title="''foobar''"></div>
+!! result
+<div title="&#39;&#39;foobar&#39;&#39;"></div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (bold)
+!! input
+<div title="'''foobar'''"></div>
+!! result
+<div title="&#39;&#39;&#39;foobar&#39;&#39;&#39;"></div>
+
+!! end
+
+
+!! test
+Bug 2304: HTML attribute safety (ISBN)
+!! input
+<div title="ISBN 1234567890"></div>
+!! result
+<div title="&#73;SBN 1234567890"></div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (RFC)
+!! input
+<div title="RFC 1234"></div>
+!! result
+<div title="&#82;FC 1234"></div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (PMID)
+!! input
+<div title="PMID 1234567890"></div>
+!! result
+<div title="&#80;MID 1234567890"></div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (web link)
+!! input
+<div title="http://example.com/"></div>
+!! result
+<div title="http&#58;//example.com/"></div>
+
+!! end
+
+!! test
+Bug 2304: HTML attribute safety (named web link)
+!! input
+<div title="[http://example.com/ link]"></div>
+!! result
+<div title="&#91;http&#58;//example.com/ link]"></div>
+
+!! end
+
+!! test
+Bug 3244: HTML attribute safety (extension; safe)
+!! input
+<div style="<nowiki>background:blue</nowiki>"></div>
+!! result
+<div style="background:blue"></div>
+
+!! end
+
+!! test
+Bug 3244: HTML attribute safety (extension; unsafe)
+!! input
+<div style="<nowiki>border-left:expression(alert(document.cookie))</nowiki>"></div>
+!! result
+<div style="/* insecure input */"></div>
+
+!! end
+
+# More MSIE fun discovered by Tom Gilder
+
+!! test
+MSIE CSS safety test: spurious slash
+!! input
+<div style="background-image:u\rl(javascript:alert('boo'))">evil</div>
+!! result
+<div style="/* insecure input */">evil</div>
+
+!! end
+
+!! test
+MSIE CSS safety test: hex code
+!! input
+<div style="background-image:u\72l(javascript:alert('boo'))">evil</div>
+!! result
+<div style="/* insecure input */">evil</div>
+
+!! end
+
+!! test
+MSIE CSS safety test: comment in url
+!! input
+<div style="background-image:u/**/rl(javascript:alert('boo'))">evil</div>
+!! result
+<div style="background-image:u rl(javascript:alert(&#39;boo&#39;))">evil</div>
+
+!! end
+
+!! test
+MSIE CSS safety test: comment in expression
+!! input
+<div style="background-image:expres/**/sion(alert('boo4'))">evil4</div>
+!! result
+<div style="background-image:expres sion(alert(&#39;boo4&#39;))">evil4</div>
+
+!! end
+
+
+!! test
+Table attribute legitimate extension
+!! input
+{|
+!+ style="<nowiki>color:blue</nowiki>"| status
+|}
+!! result
+<table>
+<tr>
+<th style="color:blue"> status
+</th></tr></table>
+
+!!end
+
+!! test
+Table attribute safety
+!! input
+{|
+!+ style="<nowiki>border-width:expression(0+alert(document.cookie))</nowiki>"| status
+|}
+!! result
+<table>
+<tr>
+<th style="/* insecure input */"> status
+</th></tr></table>
+
+!! end
+
+!! test
+CSS line continuation 1
+!! input
+<div style="background-image: u\&#10;rl(test.jpg);"></div>
+!! result
+<div style="/* insecure input */"></div>
+
+!! end
+
+!! test
+CSS line continuation 2
+!! input
+<div style="background-image: u\&#13;rl(test.jpg); "></div>
+!! result
+<div style="/* insecure input */"></div>
+
+!! end
+
+!! article
+Template:Identity
+!! text
+{{{1}}}
+!! endarticle
+
+!! test
+Expansion of multi-line templates in attribute values (bug 6255)
+!! input
+<div style="background: {{identity|#00FF00}}">-</div>
+!! result
+<div style="background: #00FF00">-</div>
+
+!! end
+
+
+!! test
+Expansion of multi-line templates in attribute values (bug 6255 sanity check)
+!! input
+<div style="background:
+#00FF00">-</div>
+!! result
+<div style="background: #00FF00">-</div>
+
+!! end
+
+!! test
+Expansion of multi-line templates in attribute values (bug 6255 sanity check 2)
+!! input
+<div style="background: &#10;#00FF00">-</div>
+!! result
+<div style="background: &#10;#00FF00">-</div>
+
+!! end
+
+###
+### Parser hooks (see maintenance/parserTestsParserHook.php for the <tag> extension)
+###
+!! test
+Parser hook: empty input
+!! input
+<tag></tag>
+!! result
+<pre>
+string(0) ""
+array(0) {
+}
+</pre>
+
+!! end
+
+!! test
+Parser hook: empty input using terminated empty elements
+!! input
+<tag/>
+!! result
+<pre>
+NULL
+array(0) {
+}
+</pre>
+
+!! end
+
+!! test
+Parser hook: empty input using terminated empty elements (space before)
+!! input
+<tag />
+!! result
+<pre>
+NULL
+array(0) {
+}
+</pre>
+
+!! end
+
+!! test
+Parser hook: basic input
+!! input
+<tag>input</tag>
+!! result
+<pre>
+string(5) "input"
+array(0) {
+}
+</pre>
+
+!! end
+
+
+!! test
+Parser hook: case insensitive
+!! input
+<TAG>input</TAG>
+!! result
+<pre>
+string(5) "input"
+array(0) {
+}
+</pre>
+
+!! end
+
+
+!! test
+Parser hook: case insensitive, redux
+!! input
+<TaG>input</TAg>
+!! result
+<pre>
+string(5) "input"
+array(0) {
+}
+</pre>
+
+!! end
+
+!! test
+Parser hook: nested tags
+!! options
+noxml
+!! input
+<tag><tag></tag></tag>
+!! result
+<pre>
+string(5) "<tag>"
+array(0) {
+}
+</pre>&lt;/tag&gt;
+
+!! end
+
+!! test
+Parser hook: basic arguments
+!! input
+<tag width=200 height = "100" depth = '50' square></tag>
+!! result
+<pre>
+string(0) ""
+array(4) {
+ ["width"]=>
+ string(3) "200"
+ ["height"]=>
+ string(3) "100"
+ ["depth"]=>
+ string(2) "50"
+ ["square"]=>
+ string(6) "square"
+}
+</pre>
+
+!! end
+
+!! test
+Parser hook: argument containing a forward slash (bug 5344)
+!! input
+<tag filename='/tmp/bla'></tag>
+!! result
+<pre>
+string(0) ""
+array(1) {
+ ["filename"]=>
+ string(8) "/tmp/bla"
+}
+</pre>
+
+!! end
+
+!! test
+Parser hook: empty input using terminated empty elements (bug 2374)
+!! input
+<tag foo=bar/>text
+!! result
+<pre>
+NULL
+array(1) {
+ ["foo"]=>
+ string(3) "bar"
+}
+</pre>text
+
+!! end
+
+# </tag> should be output literally since there is no matching tag that begins it
+!! test
+Parser hook: basic arguments using terminated empty elements (bug 2374)
+!! input
+<tag width=200 height = "100" depth = '50' square/>
+other stuff
+</tag>
+!! result
+<pre>
+NULL
+array(4) {
+ ["width"]=>
+ string(3) "200"
+ ["height"]=>
+ string(3) "100"
+ ["depth"]=>
+ string(2) "50"
+ ["square"]=>
+ string(6) "square"
+}
+</pre>
+<p>other stuff
+&lt;/tag&gt;
+</p>
+!! end
+
+###
+### (see maintenance/parserTestsStaticParserHook.php for the <statictag> extension)
+###
+
+!! test
+Parser hook: static parser hook not inside a comment
+!! input
+<statictag>hello, world</statictag>
+<statictag action=flush/>
+!! result
+<p>hello, world
+</p>
+!! end
+
+
+!! test
+Parser hook: static parser hook inside a comment
+!! input
+<!-- <statictag>hello, world</statictag> -->
+<statictag action=flush/>
+!! result
+<p><br />
+</p>
+!! end
+
+# Nested template calls; this case was broken by Parser.php rev 1.506,
+# since reverted.
+
+!! article
+Template:One-parameter
+!! text
+(My parameter is: {{{1}}})
+!! endarticle
+
+!! article
+Template:Map-one-parameter
+!! text
+{{{{{1}}}|{{{2}}}}}
+!! endarticle
+
+!! test
+Nested template calls
+!! input
+{{Map-one-parameter|One-parameter|param}}
+!! result
+<p>(My parameter is: param)
+</p>
+!! end
+
+
+###
+### Sanitizer
+###
+!! test
+Sanitizer: Closing of open tags
+!! input
+<s></s><table></table>
+!! result
+<s></s><table></table>
+
+!! end
+
+!! test
+Sanitizer: Closing of open but not closed tags
+!! input
+<s>foo
+!! result
+<p><s>foo</s>
+</p>
+!! end
+
+!! test
+Sanitizer: Closing of closed but not open tags
+!! input
+</s>
+!! result
+<p>&lt;/s&gt;
+</p>
+!! end
+
+!! test
+Sanitizer: Closing of closed but not open table tags
+!! input
+Table not started</td></tr></table>
+!! result
+<p>Table not started&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+</p>
+!! end
+
+!! test
+Sanitizer: Escaping of spaces, multibyte characters, colons & other stuff in id=""
+!! input
+<span id="æ: v">byte</span>[[#æ: v|backlink]]
+!! result
+<p><span id=".C3.A6:_v">byte</span><a href="#.C3.A6:_v">backlink</a>
+</p>
+!! end
+
+!! test
+Sanitizer: Validating the contents of the id attribute (bug 4515)
+!! options
+disabled
+!! input
+<br id=9 />
+!! result
+Something, but definitely not <br id="9" />...
+!! end
+
+!! test
+Sanitizer: Validating id attribute uniqueness (bug 4515, bug 6301)
+!! options
+disabled
+!! input
+<br id="foo" /><br id="foo" />
+!! result
+Something need to be done. foo-2 ?
+!! end
+
+!! test
+Language converter: output gets cut off unexpectedly (bug 5757)
+!! options
+language=zh
+!! input
+this bit is safe: }-
+
+but if we add a conversion instance: -{zh-cn:xxx;zh-tw:yyy}-
+
+then we get cut off here: }-
+
+all additional text is vanished
+!! result
+<p>this bit is safe: }-
+</p><p>but if we add a conversion instance: xxx
+</p><p>then we get cut off here: }-
+</p><p>all additional text is vanished
+</p>
+!! end
+
+!! test
+Self closed html pairs (bug 5487)
+!! options
+!! input
+<center><font id="bug" />Centered text</center>
+<div><font id="bug2" />In div text</div>
+!! result
+<center>&lt;font id="bug" /&gt;Centered text</center>
+<div>&lt;font id="bug2" /&gt;In div text</div>
+
+!! end
+
+#
+#
+#
+
+!! test
+Punctuation: nbsp before exclamation
+!! input
+C'est grave !
+!! result
+<p>C'est grave&#160;!
+</p>
+!! end
+
+!! test
+Punctuation: CSS !important (bug 11874)
+!! input
+<div style="width:50% !important">important</div>
+!! result
+<div style="width:50% !important">important</div>
+
+!!end
+
+!! test
+Punctuation: CSS ! important (bug 11874; with space after)
+!! input
+<div style="width:50% ! important">important</div>
+!! result
+<div style="width:50% ! important">important</div>
+
+!!end
+
+
+!! test
+HTML bullet list, closed tags (bug 5497)
+!! input
+<ul>
+<li>One</li>
+<li>Two</li>
+</ul>
+!! result
+<ul>
+<li>One</li>
+<li>Two</li>
+</ul>
+
+!! end
+
+!! test
+HTML bullet list, unclosed tags (bug 5497)
+!! options
+disabled
+!! input
+<ul>
+<li>One
+<li>Two
+</ul>
+!! result
+<ul>
+<li>One
+</li><li>Two
+</li></ul>
+
+!! end
+
+!! test
+HTML ordered list, closed tags (bug 5497)
+!! input
+<ol>
+<li>One</li>
+<li>Two</li>
+</ol>
+!! result
+<ol>
+<li>One</li>
+<li>Two</li>
+</ol>
+
+!! end
+
+!! test
+HTML ordered list, unclosed tags (bug 5497)
+!! options
+disabled
+!! input
+<ol>
+<li>One
+<li>Two
+</ol>
+!! result
+<ol>
+<li>One
+</li><li>Two
+</li></ol>
+
+!! end
+
+!! test
+HTML nested bullet list, closed tags (bug 5497)
+!! input
+<ul>
+<li>One</li>
+<li>Two:
+<ul>
+<li>Sub-one</li>
+<li>Sub-two</li>
+</ul>
+</li>
+</ul>
+!! result
+<ul>
+<li>One</li>
+<li>Two:
+<ul>
+<li>Sub-one</li>
+<li>Sub-two</li>
+</ul>
+</li>
+</ul>
+
+!! end
+
+!! test
+HTML nested bullet list, open tags (bug 5497)
+!! options
+disabled
+!! input
+<ul>
+<li>One
+<li>Two:
+<ul>
+<li>Sub-one
+<li>Sub-two
+</ul>
+</ul>
+!! result
+<ul>
+<li>One
+</li><li>Two:
+<ul>
+<li>Sub-one
+</li><li>Sub-two
+</li></ul>
+</li></ul>
+
+!! end
+
+!! test
+HTML nested ordered list, closed tags (bug 5497)
+!! input
+<ol>
+<li>One</li>
+<li>Two:
+<ol>
+<li>Sub-one</li>
+<li>Sub-two</li>
+</ol>
+</li>
+</ol>
+!! result
+<ol>
+<li>One</li>
+<li>Two:
+<ol>
+<li>Sub-one</li>
+<li>Sub-two</li>
+</ol>
+</li>
+</ol>
+
+!! end
+
+!! test
+HTML nested ordered list, open tags (bug 5497)
+!! options
+disabled
+!! input
+<ol>
+<li>One
+<li>Two:
+<ol>
+<li>Sub-one
+<li>Sub-two
+</ol>
+</ol>
+!! result
+<ol>
+<li>One
+</li><li>Two:
+<ol>
+<li>Sub-one
+</li><li>Sub-two
+</li></ol>
+</li></ol>
+
+!! end
+
+!! test
+HTML ordered list item with parameters oddity
+!! input
+<ol><li id="fragment">One</li></ol>
+!! result
+<ol><li id="fragment">One</li></ol>
+
+!! end
+
+!!test
+bug 5918: autonumbering
+!! input
+[http://first/] [http://second] [ftp://ftp]
+
+ftp://inlineftp
+
+[mailto:enclosed@mail.tld With target]
+
+[mailto:enclosed@mail.tld]
+
+mailto:inline@mail.tld
+!! result
+<p><a rel="nofollow" class="external autonumber" href="http://first/">[1]</a> <a rel="nofollow" class="external autonumber" href="http://second">[2]</a> <a rel="nofollow" class="external autonumber" href="ftp://ftp">[3]</a>
+</p><p><a rel="nofollow" class="external free" href="ftp://inlineftp">ftp://inlineftp</a>
+</p><p><a rel="nofollow" class="external text" href="mailto:enclosed@mail.tld">With target</a>
+</p><p><a rel="nofollow" class="external autonumber" href="mailto:enclosed@mail.tld">[4]</a>
+</p><p><a rel="nofollow" class="external free" href="mailto:inline@mail.tld">mailto:inline@mail.tld</a>
+</p>
+!! end
+
+
+#
+# Security and HTML correctness
+# From Nick Jenkins' fuzz testing
+#
+
+!! test
+Fuzz testing: Parser13
+!! input
+{|
+| http://a|
+!! result
+<table>
+<tr>
+<td>
+</td>
+</tr>
+</table>
+
+!! end
+
+!! test
+Fuzz testing: Parser14
+!! input
+== onmouseover= ==
+http://__TOC__
+!! result
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: onmouseover=">edit</a>]</span> <span class="mw-headline" id="onmouseover.3D"> onmouseover= </span></h2>
+http://<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#onmouseover.3D"><span class="tocnumber">1</span> <span class="toctext">onmouseover=</span></a></li>
+</ul>
+</td></tr></table>
+
+!! end
+
+!! test
+Fuzz testing: Parser14-table
+!! input
+==a==
+{| STYLE=__TOC__
+!! result
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: a">edit</a>]</span> <span class="mw-headline" id="a">a</span></h2>
+<table style="&#95;_TOC&#95;_">
+<tr><td></td></tr>
+</table>
+
+!! end
+
+# Known to produce bogus xml (extra </td>)
+!! test
+Fuzz testing: Parser16
+!! options
+noxml
+!! input
+{|
+!https://||||||
+!! result
+<table>
+<tr>
+<th>https://</th>
+<th></th>
+<th></th>
+<th>
+</td>
+</tr>
+</table>
+
+!! end
+
+!! test
+Fuzz testing: Parser21
+!! input
+{|
+! irc://{{ftp://a" onmouseover="alert('hello world');"
+|
+!! result
+<table>
+<tr>
+<th> <a rel="nofollow" class="external free" href="irc://{{ftp://a">irc://{{ftp://a</a>" onmouseover="alert('hello world');"
+</th>
+<td>
+</td>
+</tr>
+</table>
+
+!! end
+
+!! test
+Fuzz testing: Parser22
+!! input
+http://===r:::https://b
+
+{|
+!!result
+<p><a rel="nofollow" class="external free" href="http://===r:::https://b">http://===r:::https://b</a>
+</p>
+<table>
+<tr><td></td></tr>
+</table>
+
+!! end
+
+# Known to produce bad XML for now
+!! test
+Fuzz testing: Parser24
+!! options
+noxml
+!! input
+{|
+{{{|
+<u CLASS=
+| {{{{SSSll!!!!!!!VVVV)]]][[Special:*xxxxxxx--><noinclude>}}}} >
+<br style="onmouseover='alert(document.cookie);' " />
+
+MOVE YOUR MOUSE CURSOR OVER THIS TEXT
+|
+!! result
+<table>
+{{{|
+<u class="&#124;">}}}} &gt;
+<br style="onmouseover=&#39;alert(document.cookie);&#39;" />
+
+MOVE YOUR MOUSE CURSOR OVER THIS TEXT
+<tr>
+<td></u>
+</td>
+</tr>
+</table>
+
+!! end
+
+# Note: the current result listed for this is not what the original one was,
+# but the original bug was JavaScript injection, which is fixed in any case.
+# It's not clear that the original result listed was any more correct than the
+# current one. Original result:
+# <p>{{{|
+# </p>
+# <li class="&#124;&#124;">
+# }}}blah" onmouseover="alert('hello world');" align="left"<b>MOVE MOUSE CURSOR OVER HERE</b>
+!!test
+Fuzz testing: Parser25 (bug 6055)
+!! input
+{{{
+|
+<LI CLASS=||
+ >
+}}}blah" onmouseover="alert('hello world');" align="left"'''MOVE MOUSE CURSOR OVER HERE
+!! result
+<p>&lt;LI CLASS=blah" onmouseover="alert('hello world');" align="left"<b>MOVE MOUSE CURSOR OVER HERE</b>
+</p>
+!! end
+
+!!test
+Fuzz testing: URL adjacent extension (with space, clean)
+!! options
+!! input
+http://example.com <nowiki>junk</nowiki>
+!! result
+<p><a rel="nofollow" class="external free" href="http://example.com">http://example.com</a> junk
+</p>
+!!end
+
+!!test
+Fuzz testing: URL adjacent extension (no space, dirty; nowiki)
+!! options
+!! input
+http://example.com<nowiki>junk</nowiki>
+!! result
+<p><a rel="nofollow" class="external free" href="http://example.com">http://example.com</a>junk
+</p>
+!!end
+
+!!test
+Fuzz testing: URL adjacent extension (no space, dirty; pre)
+!! options
+!! input
+http://example.com<pre>junk</pre>
+!! result
+<a rel="nofollow" class="external free" href="http://example.com">http://example.com</a><pre>junk</pre>
+
+!!end
+
+!!test
+Fuzz testing: image with bogus manual thumbnail
+!!input
+[[Image:foobar.jpg|thumbnail= ]]
+!!result
+<div class="thumb tright"><div class="thumbinner" style="width:1943px;">Error creating thumbnail: <div class="thumbcaption"></div></div></div>
+
+!!end
+
+!! test
+Fuzz testing: encoded newline in generated HTML replacements (bug 6577)
+!! input
+<pre dir="&#10;"></pre>
+!! result
+<pre dir="&#10;"></pre>
+
+!! end
+
+!! test
+Parsing optional HTML elements (Bug 6171)
+!! options
+!! input
+<table>
+ <tr>
+ <td> Some tabular data</td>
+ <td> More tabular data ...
+ <td> And yet som tabular data</td>
+ </tr>
+</table>
+!! result
+<table>
+ <tr>
+ <td> Some tabular data</td>
+ <td> More tabular data ...
+ </td><td> And yet som tabular data</td>
+ </tr>
+</table>
+
+!! end
+
+!! test
+Correct handling of <td>, <tr> (Bug 6171)
+!! options
+!! input
+<table>
+ <tr>
+ <td> Some tabular data</td>
+ <td> More tabular data ...</td>
+ <td> And yet som tabular data</td>
+ </tr>
+</table>
+!! result
+<table>
+ <tr>
+ <td> Some tabular data</td>
+ <td> More tabular data ...</td>
+ <td> And yet som tabular data</td>
+ </tr>
+</table>
+
+!! end
+
+
+!! test
+Parsing crashing regression (fr:JavaScript)
+!! input
+</body></x>
+!! result
+<p>&lt;/body&gt;&lt;/x&gt;
+</p>
+!! end
+
+!! test
+Inline wiki vs wiki block nesting
+!! input
+'''Bold paragraph
+
+New wiki paragraph
+!! result
+<p><b>Bold paragraph</b>
+</p><p>New wiki paragraph
+</p>
+!! end
+
+!! test
+Inline HTML vs wiki block nesting
+!! options
+disabled
+!! input
+<b>Bold paragraph
+
+New wiki paragraph
+!! result
+<p><b>Bold paragraph</b>
+</p><p>New wiki paragraph
+</p>
+!! end
+
+# Original result was this:
+# <p><b>bold</b><b>bold<i>bolditalics</i></b>
+# </p>
+# While that might be marginally more intuitive, maybe, the six-apostrophe
+# construct is clearly pathological and the result stated here (which is what
+# the parser actually does) is about as reasonable as anything.
+!!test
+Mixing markup for italics and bold
+!! options
+!! input
+'''bold''''''bold''bolditalics'''''
+!! result
+<p>'<i>bold'</i><b>bold<i>bolditalics</i></b>
+</p>
+!! end
+
+
+!! article
+Xyzzyx
+!! text
+Article for special page transclusion test
+!! endarticle
+
+!! test
+Special page transclusion
+!! options
+!! input
+{{Special:Prefixindex/Xyzzyx}}
+!! result
+<p><br />
+</p>
+<table border="0" id="mw-prefixindex-list-table"><tr><td><a href="/wiki/Xyzzyx" title="Xyzzyx">Xyzzyx</a></td></tr></table>
+
+!! end
+
+!! test
+Special page transclusion twice (bug 5021)
+!! options
+!! input
+{{Special:Prefixindex/Xyzzyx}}
+{{Special:Prefixindex/Xyzzyx}}
+!! result
+<p><br />
+</p>
+<table border="0" id="mw-prefixindex-list-table"><tr><td><a href="/wiki/Xyzzyx" title="Xyzzyx">Xyzzyx</a></td></tr></table>
+<p><br />
+</p>
+<table border="0" id="mw-prefixindex-list-table"><tr><td><a href="/wiki/Xyzzyx" title="Xyzzyx">Xyzzyx</a></td></tr></table>
+
+!! end
+
+!! test
+Transclusion of default MediaWiki message
+!! input
+{{MediaWiki:Mainpage}}
+!!result
+<p>Main Page
+</p>
+!! end
+
+!! test
+Transclusion of nonexistent MediaWiki message
+!! input
+{{MediaWiki:Mainpagexxx}}
+!!result
+<p><a href="/index.php?title=MediaWiki:Mainpagexxx&amp;action=edit&amp;redlink=1" class="new" title="MediaWiki:Mainpagexxx (page does not exist)">MediaWiki:Mainpagexxx</a>
+</p>
+!! end
+
+!! test
+Transclusion of MediaWiki message with underscore
+!! input
+{{MediaWiki:history_short}}
+!! result
+<p>History
+</p>
+!! end
+
+!! test
+Transclusion of MediaWiki message with space
+!! input
+{{MediaWiki:history short}}
+!! result
+<p>History
+</p>
+!! end
+
+!! test
+Invalid header with following text
+!! input
+= x = y
+!! result
+<p>= x = y
+</p>
+!! end
+
+
+!! test
+Section extraction test (section 0)
+!! options
+section=0
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+start
+!! end
+
+!! test
+Section extraction test (section 1)
+!! options
+section=1
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+==a==
+===aa===
+====aaa====
+!! end
+
+!! test
+Section extraction test (section 2)
+!! options
+section=2
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+===aa===
+====aaa====
+!! end
+
+!! test
+Section extraction test (section 3)
+!! options
+section=3
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+====aaa====
+!! end
+
+!! test
+Section extraction test (section 4)
+!! options
+section=4
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+!! end
+
+!! test
+Section extraction test (section 5)
+!! options
+section=5
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+===ba===
+!! end
+
+!! test
+Section extraction test (section 6)
+!! options
+section=6
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+===bb===
+====bba====
+!! end
+
+!! test
+Section extraction test (section 7)
+!! options
+section=7
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+====bba====
+!! end
+
+!! test
+Section extraction test (section 8)
+!! options
+section=8
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+===bc===
+!! end
+
+!! test
+Section extraction test (section 9)
+!! options
+section=9
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+==c==
+===ca===
+!! end
+
+!! test
+Section extraction test (section 10)
+!! options
+section=10
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+===ca===
+!! end
+
+!! test
+Section extraction test (nonexistent section 11)
+!! options
+section=11
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+!! end
+
+!! test
+Section extraction test with bogus heading (section 1)
+!! options
+section=1
+!! input
+==a==
+==bogus== not a legal section
+==b==
+!! result
+==a==
+==bogus== not a legal section
+!! end
+
+!! test
+Section extraction test with bogus heading (section 2)
+!! options
+section=2
+!! input
+==a==
+==bogus== not a legal section
+==b==
+!! result
+==b==
+!! end
+
+!! test
+Section extraction test with comment after heading (section 1)
+!! options
+section=1
+!! input
+==a==
+==b== <!-- -->
+==c==
+!! result
+==a==
+!! end
+
+!! test
+Section extraction test with comment after heading (section 2)
+!! options
+section=2
+!! input
+==a==
+==b== <!-- -->
+==c==
+!! result
+==b== <!-- -->
+!! end
+
+!! test
+Section extraction test with bogus <nowiki> heading (section 1)
+!! options
+section=1
+!! input
+==a==
+==bogus== <nowiki>not a legal section</nowiki>
+==b==
+!! result
+==a==
+==bogus== <nowiki>not a legal section</nowiki>
+!! end
+
+!! test
+Section extraction test with bogus <nowiki> heading (section 2)
+!! options
+section=2
+!! input
+==a==
+==bogus== <nowiki>not a legal section</nowiki>
+==b==
+!! result
+==b==
+!! end
+
+
+# Formerly testing for bug 2587, now resolved by the use of unmarked sections
+# instead of respecting commented sections
+!! test
+Section extraction prefixed by comment (section 1)
+!! options
+section=1
+!! input
+<!-- -->==sec1==
+==sec2==
+!!result
+==sec2==
+!!end
+
+!! test
+Section extraction prefixed by comment (section 2)
+!! options
+section=2
+!! input
+<!-- -->==sec1==
+==sec2==
+!!result
+
+!!end
+
+
+# Formerly testing for bug 2607, now resolved by the use of unmarked sections
+# instead of respecting HTML-style headings
+!! test
+Section extraction, mixed wiki and html (section 1)
+!! options
+section=1
+!! input
+<h2>unmarked</h2>
+unmarked
+==1==
+one
+==2==
+two
+!! result
+==1==
+one
+!! end
+
+!! test
+Section extraction, mixed wiki and html (section 2)
+!! options
+section=2
+!! input
+<h2>unmarked</h2>
+unmarked
+==1==
+one
+==2==
+two
+!! result
+==2==
+two
+!! end
+
+
+# Formerly testing for bug 3342
+!! test
+Section extraction, heading surrounded by <noinclude>
+!! options
+section=1
+!! input
+<noinclude>==unmarked==</noinclude>
+==marked==
+!! result
+==marked==
+!!end
+
+# Test behaviour of bug 19910
+!! test
+Sectiion with all-equals
+!! options
+section=2
+!! input
+===
+The line above must have a trailing space
+=== <!--
+--> <!-- -->
+But just in case it doesn't...
+!! result
+=== <!--
+--> <!-- -->
+But just in case it doesn't...
+!! end
+
+!! test
+Section replacement test (section 0)
+!! options
+replace=0,"xxx"
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+xxx
+
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! end
+
+!! test
+Section replacement test (section 1)
+!! options
+replace=1,"xxx"
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+start
+xxx
+
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! end
+
+!! test
+Section replacement test (section 2)
+!! options
+replace=2,"xxx"
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+start
+==a==
+xxx
+
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! end
+
+!! test
+Section replacement test (section 3)
+!! options
+replace=3,"xxx"
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+start
+==a==
+===aa===
+xxx
+
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! end
+
+!! test
+Section replacement test (section 4)
+!! options
+replace=4,"xxx"
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+start
+==a==
+===aa===
+====aaa====
+xxx
+
+==c==
+===ca===
+!! end
+
+!! test
+Section replacement test (section 5)
+!! options
+replace=5,"xxx"
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+start
+==a==
+===aa===
+====aaa====
+==b==
+xxx
+
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! end
+
+!! test
+Section replacement test (section 6)
+!! options
+replace=6,"xxx"
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+xxx
+
+===bc===
+==c==
+===ca===
+!! end
+
+!! test
+Section replacement test (section 7)
+!! options
+replace=7,"xxx"
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+xxx
+
+===bc===
+==c==
+===ca===
+!! end
+
+!! test
+Section replacement test (section 8)
+!! options
+replace=8,"xxx"
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+xxx
+
+==c==
+===ca===
+!!end
+
+!! test
+Section replacement test (section 9)
+!! options
+replace=9,"xxx"
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+xxx
+!! end
+
+!! test
+Section replacement test (section 10)
+!! options
+replace=10,"xxx"
+!! input
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+===ca===
+!! result
+start
+==a==
+===aa===
+====aaa====
+==b==
+===ba===
+===bb===
+====bba====
+===bc===
+==c==
+xxx
+!! end
+
+!! test
+Section replacement test with initial whitespace (bug 13728)
+!! options
+replace=2,"xxx"
+!! input
+ Preformatted initial line
+==a==
+===a===
+!! result
+ Preformatted initial line
+==a==
+xxx
+!! end
+
+
+!! test
+Section extraction, heading followed by pre with 20 spaces (bug 6398)
+!! options
+section=1
+!! input
+==a==
+ a
+!! result
+==a==
+ a
+!! end
+
+!! test
+Section extraction, heading followed by pre with 19 spaces (bug 6398 sanity check)
+!! options
+section=1
+!! input
+==a==
+ a
+!! result
+==a==
+ a
+!! end
+
+
+!! test
+Section extraction, <pre> around bogus header (bug 10309)
+!! options
+noxml section=2
+!! input
+== Section One ==
+<pre>
+=======
+</pre>
+
+== Section Two ==
+stuff
+!! result
+== Section Two ==
+stuff
+!! end
+
+!! test
+Section replacement, <pre> around bogus header (bug 10309)
+!! options
+noxml replace=2,"xxx"
+!! input
+== Section One ==
+<pre>
+=======
+</pre>
+
+== Section Two ==
+stuff
+!! result
+== Section One ==
+<pre>
+=======
+</pre>
+
+xxx
+!! end
+
+
+
+!! test
+Handling of &#x0A; in URLs
+!! input
+**irc://&#x0A;a
+!! result
+<ul><li><ul><li><a rel="nofollow" class="external free" href="irc://%0Aa">irc://%0Aa</a>
+</li></ul>
+</li></ul>
+
+!!end
+
+!! test
+5 quotes, code coverage +1 line
+!! input
+'''''
+!! result
+!! end
+
+!! test
+Special:Search page linking.
+!! input
+{{Special:search}}
+!! result
+<p><a href="/wiki/Special:Search" title="Special:Search">Special:Search</a>
+</p>
+!! end
+
+!! test
+Say the magic word
+!! input
+* {{PAGENAME}}
+* {{BASEPAGENAME}}
+* {{SUBPAGENAME}}
+* {{SUBPAGENAMEE}}
+* {{BASEPAGENAME}}
+* {{BASEPAGENAMEE}}
+* {{TALKPAGENAME}}
+* {{TALKPAGENAMEE}}
+* {{SUBJECTPAGENAME}}
+* {{SUBJECTPAGENAMEE}}
+* {{NAMESPACEE}}
+* {{NAMESPACE}}
+* {{TALKSPACE}}
+* {{TALKSPACEE}}
+* {{SUBJECTSPACE}}
+* {{SUBJECTSPACEE}}
+* {{Dynamic|{{NUMBEROFUSERS}}|{{NUMBEROFPAGES}}|{{CURRENTVERSION}}|{{CONTENTLANGUAGE}}|{{DIRECTIONMARK}}|{{CURRENTTIMESTAMP}}|{{NUMBEROFARTICLES}}}}
+!! result
+<ul><li> Parser test
+</li><li> Parser test
+</li><li> Parser test
+</li><li> Parser_test
+</li><li> Parser test
+</li><li> Parser_test
+</li><li> Talk:Parser test
+</li><li> Talk:Parser_test
+</li><li> Parser test
+</li><li> Parser_test
+</li><li>
+</li><li>
+</li><li> Talk
+</li><li> Talk
+</li><li>
+</li><li>
+</li><li> <a href="/index.php?title=Template:Dynamic&amp;action=edit&amp;redlink=1" class="new" title="Template:Dynamic (page does not exist)">Template:Dynamic</a>
+</li></ul>
+
+!! end
+### Note: Above tests excludes the "{{NUMBEROFADMINS}}" magic word because it generates a MySQL error when included.
+
+!! test
+Gallery
+!! input
+<gallery>
+image1.png |
+image2.gif|||||
+
+image3|
+image4 |300px| centre
+ image5.svg| http://///////
+[[x|xx]]]]
+* image6
+</gallery>
+!! result
+<ul class="gallery">
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div style="height: 150px;">Image1.png</div>
+ <div class="gallerytext">
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div style="height: 150px;">Image2.gif</div>
+ <div class="gallerytext">
+<p>||||
+</p>
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div style="height: 150px;">Image3</div>
+ <div class="gallerytext">
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div style="height: 150px;">Image4</div>
+ <div class="gallerytext">
+<p>300px| centre
+</p>
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div style="height: 150px;">Image5.svg</div>
+ <div class="gallerytext">
+<p><a rel="nofollow" class="external free" href="http://///////">http://///////</a>
+</p>
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div style="height: 150px;">* image6</div>
+ <div class="gallerytext">
+ </div>
+ </div></li>
+</ul>
+
+!! end
+
+!! test
+Gallery (with options)
+!! input
+<gallery widths='70px' heights='40px' perrow='2' caption='Foo [[Main Page]]' >
+File:Nonexistant.jpg|caption
+File:Nonexistant.jpg
+image:foobar.jpg|some '''caption''' [[Main Page]]
+image:foobar.jpg
+image:foobar.jpg|Blabla|alt=This is a foo-bar.|blabla.
+</gallery>
+!! result
+<ul class="gallery" style="max-width: 226px;_width: 226px;">
+ <li class='gallerycaption'>Foo <a href="/wiki/Main_Page" title="Main Page">Main Page</a></li>
+ <li class="gallerybox" style="width: 105px"><div style="width: 105px">
+ <div style="height: 70px;">Nonexistant.jpg</div>
+ <div class="gallerytext">
+<p>caption
+</p>
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 105px"><div style="width: 105px">
+ <div style="height: 70px;">Nonexistant.jpg</div>
+ <div class="gallerytext">
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 105px"><div style="width: 105px">
+ <div class="thumb" style="width: 100px;"><div style="margin:31px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="70" height="8" /></a></div></div>
+ <div class="gallerytext">
+<p>some <b>caption</b> <a href="/wiki/Main_Page" title="Main Page">Main Page</a>
+</p>
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 105px"><div style="width: 105px">
+ <div class="thumb" style="width: 100px;"><div style="margin:31px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="70" height="8" /></a></div></div>
+ <div class="gallerytext">
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 105px"><div style="width: 105px">
+ <div class="thumb" style="width: 100px;"><div style="margin:31px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="This is a foo-bar." src="http://example.com/images/3/3a/Foobar.jpg" width="70" height="8" /></a></div></div>
+ <div class="gallerytext">
+<p>Blabla|blabla.
+</p>
+ </div>
+ </div></li>
+</ul>
+
+!! end
+
+!! test
+Gallery with wikitext inside caption
+!! input
+<gallery>
+File:foobar.jpg|[[File:foobar.jpg|20px|desc|alt=inneralt]]|alt=galleryalt
+File:foobar.jpg|{{Test|unamedParam|alt=param}}|alt=galleryalt
+</gallery>
+!! result
+<ul class="gallery">
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="galleryalt" src="http://example.com/images/3/3a/Foobar.jpg" width="120" height="14" /></a></div></div>
+ <div class="gallerytext">
+<p><a href="/wiki/File:Foobar.jpg" class="image" title="desc"><img alt="inneralt" src="http://example.com/images/3/3a/Foobar.jpg" width="20" height="2" /></a>
+</p>
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="galleryalt" src="http://example.com/images/3/3a/Foobar.jpg" width="120" height="14" /></a></div></div>
+ <div class="gallerytext">
+<p>This is a test template
+</p>
+ </div>
+ </div></li>
+</ul>
+
+!! end
+
+!! test
+gallery (with showfilename option)
+!! input
+<gallery showfilename>
+File:Nonexistant.jpg|caption
+File:Nonexistant.jpg
+image:foobar.jpg|some '''caption''' [[Main Page]]
+File:Foobar.jpg
+</gallery>
+!! result
+<ul class="gallery">
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div style="height: 150px;">Nonexistant.jpg</div>
+ <div class="gallerytext">
+<p><a href="/wiki/File:Nonexistant.jpg" title="File:Nonexistant.jpg">Nonexistant.jpg</a><br />
+caption
+</p>
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div style="height: 150px;">Nonexistant.jpg</div>
+ <div class="gallerytext">
+<p><a href="/wiki/File:Nonexistant.jpg" title="File:Nonexistant.jpg">Nonexistant.jpg</a><br />
+</p>
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="120" height="14" /></a></div></div>
+ <div class="gallerytext">
+<p><a href="/wiki/File:Foobar.jpg" title="File:Foobar.jpg">Foobar.jpg</a><br />
+some <b>caption</b> <a href="/wiki/Main_Page" title="Main Page">Main Page</a>
+</p>
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="120" height="14" /></a></div></div>
+ <div class="gallerytext">
+<p><a href="/wiki/File:Foobar.jpg" title="File:Foobar.jpg">Foobar.jpg</a><br />
+</p>
+ </div>
+ </div></li>
+</ul>
+
+!! end
+
+!! test
+Gallery (with namespace-less filenames)
+!! input
+<gallery>
+File:Nonexistant.jpg
+Nonexistant.jpg
+image:foobar.jpg
+foobar.jpg
+</gallery>
+!! result
+<ul class="gallery">
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div style="height: 150px;">Nonexistant.jpg</div>
+ <div class="gallerytext">
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div style="height: 150px;">Nonexistant.jpg</div>
+ <div class="gallerytext">
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="120" height="14" /></a></div></div>
+ <div class="gallerytext">
+ </div>
+ </div></li>
+ <li class="gallerybox" style="width: 155px"><div style="width: 155px">
+ <div class="thumb" style="width: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="120" height="14" /></a></div></div>
+ <div class="gallerytext">
+ </div>
+ </div></li>
+</ul>
+
+!! end
+
+!! test
+HTML Hex character encoding (spells the word "JavaScript")
+!! input
+&#x4A;&#x061;&#x0076;&#x00061;&#x000053;&#x0000063;&#114;&#x0000069;&#00000112;&#x0000000074;
+!! result
+<p>&#x4a;&#x61;&#x76;&#x61;&#x53;&#x63;&#114;&#x69;&#112;&#x74;
+</p>
+!! end
+
+!! test
+HTML Hex character encoding bogus encoding (bug 26437 regression check)
+!! input
+&#xsee;&#XSEE;
+!! result
+<p>&amp;#xsee;&amp;#XSEE;
+</p>
+!! end
+
+!! test
+HTML Hex character encoding mixed case
+!! input
+&#xEE;&#Xee;
+!! result
+<p>&#xee;&#xee;
+</p>
+!! end
+
+!! test
+__FORCETOC__ override
+!! input
+__NEWSECTIONLINK__
+__FORCETOC__
+!! result
+<p><br />
+</p>
+!! end
+
+!! test
+ISBN code coverage
+!! input
+ISBN 978-0-1234-56&#x20;789
+!! result
+<p><a href="/wiki/Special:BookSources/9780123456" class="internal mw-magiclink-isbn">ISBN 978-0-1234-56</a>&#x20;789
+</p>
+!! end
+
+!! test
+ISBN followed by 5 spaces
+!! input
+ISBN
+!! result
+<p>ISBN
+</p>
+!! end
+
+!! test
+Double ISBN
+!! input
+ISBN ISBN 1234567890
+!! result
+<p>ISBN <a href="/wiki/Special:BookSources/1234567890" class="internal mw-magiclink-isbn">ISBN 1234567890</a>
+</p>
+!! end
+
+!! test
+Bug 22905: <abbr> followed by ISBN followed by </a>
+!! input
+<abbr>(fr)</abbr> ISBN 2753300917 [http://www.example.com example.com]
+!! result
+<p><abbr>(fr)</abbr> <a href="/wiki/Special:BookSources/2753300917" class="internal mw-magiclink-isbn">ISBN 2753300917</a> <a rel="nofollow" class="external text" href="http://www.example.com">example.com</a>
+</p>
+!! end
+
+!! test
+Double RFC
+!! input
+RFC RFC 1234
+!! result
+<p>RFC <a class="external mw-magiclink-rfc" href="//tools.ietf.org/html/rfc1234">RFC 1234</a>
+</p>
+!! end
+
+!! test
+Double RFC with a wiki link
+!! input
+RFC [[RFC 1234]]
+!! result
+<p>RFC <a href="/index.php?title=RFC_1234&amp;action=edit&amp;redlink=1" class="new" title="RFC 1234 (page does not exist)">RFC 1234</a>
+</p>
+!! end
+
+!! test
+RFC code coverage
+!! input
+RFC 983&#x20;987
+!! result
+<p><a class="external mw-magiclink-rfc" href="//tools.ietf.org/html/rfc983">RFC 983</a>&#x20;987
+</p>
+!! end
+
+!! test
+Centre-aligned image
+!! input
+[[Image:foobar.jpg|centre]]
+!! result
+<div class="center"><div class="floatnone"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a></div></div>
+
+!!end
+
+!! test
+None-aligned image
+!! input
+[[Image:foobar.jpg|none]]
+!! result
+<div class="floatnone"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a></div>
+
+!!end
+
+!! test
+Width + Height sized image (using px) (height is ignored)
+!! input
+[[Image:foobar.jpg|640x480px]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="640" height="73" /></a>
+</p>
+!!end
+
+!! test
+Width-sized image (using px, no following whitespace)
+!! input
+[[Image:foobar.jpg|640px]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="640" height="73" /></a>
+</p>
+!!end
+
+!! test
+Width-sized image (using px, with following whitespace - test regression from r39467)
+!! input
+[[Image:foobar.jpg|640px ]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="640" height="73" /></a>
+</p>
+!!end
+
+!! test
+Width-sized image (using px, with preceding whitespace - test regression from r39467)
+!! input
+[[Image:foobar.jpg| 640px]]
+!! result
+<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="640" height="73" /></a>
+</p>
+!!end
+
+!! test
+Another italics / bold test
+!! input
+ ''' ''x'
+!! result
+<pre>'<i> </i>x'
+</pre>
+!!end
+
+# Note the results may be incorrect, as parserTest output included this:
+# XML error: Mismatched tag at byte 6120:
+# ...<dd> </dt></dl> </dd...
+!! test
+dt/dd/dl test
+!! options
+disabled
+!! input
+:;;;::
+!! result
+<dl><dd><dl><dt><dl><dt><dl><dt><dl><dd><dl><dd>
+</dd></dl>
+</dd></dl>
+</dt></dl>
+</dt></dl>
+</dt></dl>
+</dd></dl>
+
+!!end
+
+
+# Images with the "|" character in external URLs in comment tags; Eats half the comment, leaves unmatched "</a>" tag.
+!! test
+Images with the "|" character in the comment
+!! input
+[[image:Foobar.jpg|thumb|An [http://test/?param1=|left|&param2=|x external] URL]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>An <a rel="nofollow" class="external text" href="http://test/?param1=%7Cleft%7C&amp;param2=%7Cx">external</a> URL</div></div></div>
+
+!!end
+
+!! test
+[Before] HTML without raw HTML enabled ($wgRawHtml==false)
+!! input
+<html><script>alert(1);</script></html>
+!! result
+<p>&lt;html&gt;&lt;script&gt;alert(1);&lt;/script&gt;&lt;/html&gt;
+</p>
+!! end
+
+!! test
+HTML with raw HTML ($wgRawHtml==true)
+!! options
+rawhtml
+!! input
+<html><script>alert(1);</script></html>
+!! result
+<p><script>alert(1);</script>
+</p>
+!! end
+
+!! test
+Parents of subpages, one level up
+!! options
+subpage title=[[Subpage test/L1/L2/L3]]
+!! input
+[[../|L2]]
+!! result
+<p><a href="/index.php?title=Subpage_test/L1/L2&amp;action=edit&amp;redlink=1" class="new" title="Subpage test/L1/L2 (page does not exist)">L2</a>
+</p>
+!! end
+
+
+!! test
+Parents of subpages, one level up, not named
+!! options
+subpage title=[[Subpage test/L1/L2/L3]]
+!! input
+[[../]]
+!! result
+<p><a href="/index.php?title=Subpage_test/L1/L2&amp;action=edit&amp;redlink=1" class="new" title="Subpage test/L1/L2 (page does not exist)">Subpage test/L1/L2</a>
+</p>
+!! end
+
+
+
+!! test
+Parents of subpages, two levels up
+!! options
+subpage title=[[Subpage test/L1/L2/L3]]
+!! input
+[[../../|L1]]2
+
+[[../../|L1]]l
+!! result
+<p><a href="/index.php?title=Subpage_test/L1&amp;action=edit&amp;redlink=1" class="new" title="Subpage test/L1 (page does not exist)">L1</a>2
+</p><p><a href="/index.php?title=Subpage_test/L1&amp;action=edit&amp;redlink=1" class="new" title="Subpage test/L1 (page does not exist)">L1l</a>
+</p>
+!! end
+
+!! test
+Parents of subpages, two levels up, without trailing slash or name.
+!! options
+subpage title=[[Subpage test/L1/L2/L3]]
+!! input
+[[../..]]
+!! result
+<p>[[../..]]
+</p>
+!! end
+
+!! test
+Parents of subpages, two levels up, with lots of extra trailing slashes.
+!! options
+subpage title=[[Subpage test/L1/L2/L3]]
+!! input
+[[../../////]]
+!! result
+<p><a href="/index.php?title=Subpage_test/L1////&amp;action=edit&amp;redlink=1" class="new" title="Subpage test/L1//// (page does not exist)">///</a>
+</p>
+!! end
+
+!! test
+Definition list code coverage
+!! input
+; title : def
+; title : def
+;title: def
+!! result
+<dl><dt> title &#160;</dt><dd> def
+</dd><dt> title&#160;</dt><dd> def
+</dd><dt>title</dt><dd> def
+</dd></dl>
+
+!! end
+
+!! test
+Don't fall for the self-closing div
+!! input
+<div>hello world</div/>
+!! result
+<div>hello world</div>
+
+!! end
+
+!! test
+MSGNW magic word
+!! input
+{{MSGNW:msg}}
+!! result
+<p>&#91;&#91;:Template:Msg&#93;&#93;
+</p>
+!! end
+
+!! test
+RAW magic word
+!! input
+{{RAW:QUERTY}}
+!! result
+<p><a href="/index.php?title=Template:QUERTY&amp;action=edit&amp;redlink=1" class="new" title="Template:QUERTY (page does not exist)">Template:QUERTY</a>
+</p>
+!! end
+
+# This isn't needed for XHTML conformance, but would be handy as a fallback security measure
+!! test
+Always escape literal '>' in output, not just after '<'
+!! input
+><>
+!! result
+<p>&gt;&lt;&gt;
+</p>
+!! end
+
+!! test
+Template caching
+!! input
+{{Test}}
+{{Test}}
+!! result
+<p>This is a test template
+This is a test template
+</p>
+!! end
+
+
+!! article
+MediaWiki:Fake
+!! text
+==header==
+!! endarticle
+
+!! test
+Inclusion of !userCanEdit() content
+!! input
+{{MediaWiki:Fake}}
+!! result
+<h2><span class="editsection">[<a href="/index.php?title=MediaWiki:Fake&amp;action=edit&amp;section=T-1" title="MediaWiki:Fake">edit</a>]</span> <span class="mw-headline" id="header">header</span></h2>
+
+!! end
+
+
+!! test
+Out-of-order TOC heading levels
+!! input
+==2==
+======6======
+===3===
+=1=
+=====5=====
+==2==
+!! result
+<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="#2"><span class="tocnumber">1</span> <span class="toctext">2</span></a>
+<ul>
+<li class="toclevel-2 tocsection-2"><a href="#6"><span class="tocnumber">1.1</span> <span class="toctext">6</span></a></li>
+<li class="toclevel-2 tocsection-3"><a href="#3"><span class="tocnumber">1.2</span> <span class="toctext">3</span></a></li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-4"><a href="#1"><span class="tocnumber">2</span> <span class="toctext">1</span></a>
+<ul>
+<li class="toclevel-2 tocsection-5"><a href="#5"><span class="tocnumber">2.1</span> <span class="toctext">5</span></a></li>
+<li class="toclevel-2 tocsection-6"><a href="#2_2"><span class="tocnumber">2.2</span> <span class="toctext">2</span></a></li>
+</ul>
+</li>
+</ul>
+</td></tr></table>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: 2">edit</a>]</span> <span class="mw-headline" id="2">2</span></h2>
+<h6><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: 6">edit</a>]</span> <span class="mw-headline" id="6">6</span></h6>
+<h3><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: 3">edit</a>]</span> <span class="mw-headline" id="3">3</span></h3>
+<h1><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=4" title="Edit section: 1">edit</a>]</span> <span class="mw-headline" id="1">1</span></h1>
+<h5><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=5" title="Edit section: 5">edit</a>]</span> <span class="mw-headline" id="5">5</span></h5>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=6" title="Edit section: 2">edit</a>]</span> <span class="mw-headline" id="2_2">2</span></h2>
+
+!! end
+
+
+!! test
+ISBN with a dummy number
+!! input
+ISBN ---
+!! result
+<p>ISBN ---
+</p>
+!! end
+
+
+!! test
+ISBN with space-delimited number
+!! input
+ISBN 92 9017 032 8
+!! result
+<p><a href="/wiki/Special:BookSources/9290170328" class="internal mw-magiclink-isbn">ISBN 92 9017 032 8</a>
+</p>
+!! end
+
+
+!! test
+ISBN with multiple spaces, no number
+!! input
+ISBN foo
+!! result
+<p>ISBN foo
+</p>
+!! end
+
+
+!! test
+ISBN length
+!! input
+ISBN 123456789
+
+ISBN 1234567890
+
+ISBN 12345678901
+!! result
+<p>ISBN 123456789
+</p><p><a href="/wiki/Special:BookSources/1234567890" class="internal mw-magiclink-isbn">ISBN 1234567890</a>
+</p><p>ISBN 12345678901
+</p>
+!! end
+
+
+!! test
+ISBN with trailing year (bug 8110)
+!! input
+ISBN 1-234-56789-0 - 2006
+
+ISBN 1 234 56789 0 - 2006
+!! result
+<p><a href="/wiki/Special:BookSources/1234567890" class="internal mw-magiclink-isbn">ISBN 1-234-56789-0</a> - 2006
+</p><p><a href="/wiki/Special:BookSources/1234567890" class="internal mw-magiclink-isbn">ISBN 1 234 56789 0</a> - 2006
+</p>
+!! end
+
+
+!! test
+anchorencode
+!! input
+{{anchorencode:foo bar©#%n}}
+!! result
+<p>foo_bar.C2.A9.23.25n
+</p>
+!! end
+
+!! test
+anchorencode trims spaces
+!! input
+{{anchorencode: __pretty__please__}}
+!! result
+<p>pretty_please
+</p>
+!! end
+
+!! test
+anchorencode deals with links
+!! input
+{{anchorencode: [[hello|world]] [[hi]]}}
+!! result
+<p>world_hi
+</p>
+!! end
+
+!! test
+anchorencode deals with templates
+!! input
+{{anchorencode: {{Foo}} }}
+!! result
+<p>FOO
+</p>
+!! end
+
+!! test
+anchorencode encodes like the TOC generator: (bug 18431)
+!! input
+=== _ +:.3A%3A&&amp;]] ===
+{{anchorencode: _ +:.3A%3A&&amp;]] }}
+__NOEDITSECTION__
+!! result
+<h3> <span class="mw-headline" id=".2B:.3A.253A.26.26.5D.5D"> _ +:.3A%3A&amp;&amp;]] </span></h3>
+<p>.2B:.3A.253A.26.26.5D.5D
+</p>
+!! end
+
+# Expected output in the following test is not necessarily expected (there
+# should probably be <p> tags inside the <blockquote> in the output) -- it's
+# only testing for well-formedness.
+!! test
+Bug 6200: blockquotes and paragraph formatting
+!! input
+<blockquote>
+foo
+</blockquote>
+
+bar
+
+ baz
+!! result
+<blockquote>
+foo
+</blockquote>
+<p>bar
+</p>
+<pre>baz
+</pre>
+!! end
+
+!! test
+Bug 8293: Use of center tag ruins paragraph formatting
+!! input
+<center>
+foo
+</center>
+
+bar
+
+ baz
+!! result
+<center>
+<p>foo
+</p>
+</center>
+<p>bar
+</p>
+<pre>baz
+</pre>
+!! end
+
+
+###
+### Language variants related tests
+###
+!! test
+Self-link in language variants
+!! options
+title=[[Dunav]] language=sr
+!! input
+Both [[Dunav]] and [[Дунав]] are names for this river.
+!! result
+<p>Both <strong class="selflink">Dunav</strong> and <strong class="selflink">Дунав</strong> are names for this river.
+</p>
+!!end
+
+
+!! test
+Link to pages in language variants
+!! options
+language=sr
+!! input
+Main Page can be written as [[Маин Паге]]
+!! result
+<p>Main Page can be written as <a href="/wiki/Main_Page" title="Main Page">Маин Паге</a>
+</p>
+!!end
+
+
+!! test
+Multiple links to pages in language variants
+!! options
+language=sr
+!! input
+[[Main Page]] can be written as [[Маин Паге]] same as [[Маин Паге]].
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">Main Page</a> can be written as <a href="/wiki/Main_Page" title="Main Page">Маин Паге</a> same as <a href="/wiki/Main_Page" title="Main Page">Маин Паге</a>.
+</p>
+!!end
+
+
+!! test
+Simple template in language variants
+!! options
+language=sr
+!! input
+{{теÑÑ‚}}
+!! result
+<p>This is a test template
+</p>
+!! end
+
+
+!! test
+Template with explicit namespace in language variants
+!! options
+language=sr
+!! input
+{{Template:теÑÑ‚}}
+!! result
+<p>This is a test template
+</p>
+!! end
+
+
+!! test
+Basic test for template parameter in language variants
+!! options
+language=sr
+!! input
+{{парамтеÑÑ‚|param=foo}}
+!! result
+<p>This is a test template with parameter foo
+</p>
+!! end
+
+
+!! test
+Simple category in language variants
+!! options
+language=sr cat
+!! input
+[[Category:МедиаWики УÑер'Ñ Ð“ÑƒÐ¸Ð´Ðµ]]
+!! result
+<a href="/wiki/%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%98%D0%B0:MediaWiki_User%27s_Guide" title="Категорија:MediaWiki User's Guide">MediaWiki User's Guide</a>
+!! end
+
+
+!! test
+Stripping -{}- tags (language variants)
+!! options
+language=sr
+!! input
+Latin proverb: -{Ne nuntium necare}-
+!! result
+<p>Latin proverb: Ne nuntium necare
+</p>
+!! end
+
+
+!! test
+Prevent conversion with -{}- tags (language variants)
+!! options
+language=sr variant=sr-ec
+!! input
+Latinski: -{Ne nuntium necare}-
+!! result
+<p>ЛатинÑки: Ne nuntium necare
+</p>
+!! end
+
+
+!! test
+Prevent conversion of text with -{}- tags (language variants)
+!! options
+language=sr variant=sr-ec
+!! input
+Latinski: -{Ne nuntium necare}-
+!! result
+<p>ЛатинÑки: Ne nuntium necare
+</p>
+!! end
+
+
+!! test
+Prevent conversion of links with -{}- tags (language variants)
+!! options
+language=sr variant=sr-ec
+!! input
+-{[[Main Page]]}-
+!! result
+<p><a href="/wiki/Main_Page" title="Main Page">Main Page</a>
+</p>
+!! end
+
+
+!! test
+-{}- tags within headlines (within html for parserConvert())
+!! options
+language=sr variant=sr-ec
+!! input
+== -{Naslov}- ==
+!! result
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Уредите одељак „Naslov“">уреди</a>]</span> <span class="mw-headline" id="-.7BNaslov.7D-"> Naslov </span></h2>
+
+!! end
+
+
+!! test
+Explicit definition of language variant alternatives
+!! options
+language=zh variant=zh-tw
+!! input
+-{zh:China;zh-tw:Taiwan}-, not China
+!! result
+<p>Taiwan, not China
+</p>
+!! end
+
+
+!! test
+Explicit session-wise language variant mapping (A flag and - flag)
+!! options
+language=zh variant=zh-tw
+!! input
+Taiwan is not China.
+But -{A|zh:China;zh-tw:Taiwan}- is China,
+(This-{-|zh:China;zh-tw:Taiwan}- should be stripped!)
+and -{China}- is China.
+!! result
+<p>Taiwan is not China.
+But Taiwan is Taiwan,
+(This should be stripped!)
+and China is China.
+</p>
+!! end
+
+!! test
+Explicit session-wise language variant mapping (H flag for hide)
+!! options
+language=zh variant=zh-tw
+!! input
+(This-{H|zh:China;zh-tw:Taiwan}- should be stripped!)
+Taiwan is China.
+!! result
+<p>(This should be stripped!)
+Taiwan is Taiwan.
+</p>
+!! end
+
+!! test
+Adding explicit conversion rule for title (T flag)
+!! options
+language=zh variant=zh-tw showtitle
+!! input
+Should be stripped-{T|zh:China;zh-tw:Taiwan}-!
+!! result
+Taiwan
+<p>Should be stripped!
+</p>
+!! end
+
+!! test
+Testing that changing the language variant here in the tests actually works
+!! options
+language=zh variant=zh showtitle
+!! input
+Should be stripped-{T|zh:China;zh-tw:Taiwan}-!
+!! result
+China
+<p>Should be stripped!
+</p>
+!! end
+
+!! test
+Bug 24072: more test on conversion rule for title
+!! options
+language=zh variant=zh-tw showtitle
+!! input
+This should be stripped-{T|zh:China;zh-tw:Taiwan}-!
+This won't take interferes with the title rule-{H|zh:Beijing;zh-tw:Taipei}-.
+!! result
+Taiwan
+<p>This should be stripped!
+This won't take interferes with the title rule.
+</p>
+!! end
+
+!! test
+Raw output of variant escape tags (R flag)
+!! options
+language=zh variant=zh-tw
+!! input
+Raw: -{R|zh:China;zh-tw:Taiwan}-
+!! result
+<p>Raw: zh:China;zh-tw:Taiwan
+</p>
+!! end
+
+!! test
+Nested using of manual convert syntax
+!! options
+language=zh variant=zh-hk
+!! input
+Nested: -{zh-hans:Hi -{zh-cn:China;zh-sg:Singapore;}-;zh-hant:Hello -{zh-tw:Taiwan;zh-hk:H-{ong}- K-{}-ong;}-;}-!
+!! result
+<p>Nested: Hello Hong Kong!
+</p>
+!! end
+
+!! test
+Do not convert roman numbers to language variants
+!! options
+language=sr variant=sr-ec
+!! input
+Fridrih IV je car.
+!! result
+<p>Фридрих IV је цар.
+</p>
+!! end
+
+!! test
+Unclosed language converter markup "-{"
+!! options
+language=sr
+!! input
+-{T|hello
+!! result
+<p>-{T|hello
+</p>
+!! end
+
+!! test
+Don't convert raw rule "-{R|=&gt;}-" to "=>"
+!! options
+language=sr
+!! input
+-{R|=&gt;}-
+!! result
+<p>=&gt;
+</p>
+!!end
+
+!!article
+Template:Bullet
+!!text
+* Bar
+!!endarticle
+
+!! test
+Bug 529: Uncovered bullet
+!! input
+* Foo {{bullet}}
+!! result
+<ul><li> Foo
+</li><li> Bar
+</li></ul>
+
+!! end
+
+!! test
+Bug 529: Uncovered table already at line-start
+!! input
+x
+
+{{table}}
+y
+!! result
+<p>x
+</p>
+<table>
+<tr>
+<td> 1 </td>
+<td> 2
+</td></tr>
+<tr>
+<td> 3 </td>
+<td> 4
+</td></tr></table>
+<p>y
+</p>
+!! end
+
+!! test
+Bug 529: Uncovered bullet in parser function result
+!! input
+* Foo {{lc:{{bullet}} }}
+!! result
+<ul><li> Foo
+</li><li> bar
+</li></ul>
+
+!! end
+
+!! test
+Bug 5678: Double-parsed template argument
+!! input
+{{lc:{{{1}}}|hello}}
+!! result
+<p>{{{1}}}
+</p>
+!! end
+
+!! test
+Bug 5678: Double-parsed template invocation
+!! input
+{{lc:{{paramtest {{!}} param = hello }} }}
+!! result
+<p>{{paramtest | param = hello }}
+</p>
+!! end
+
+!! test
+Case insensitivity of parser functions for non-ASCII characters (bug 8143)
+!! options
+language=cs
+title=[[Main Page]]
+!! input
+{{PRVNÃVELKÉ:ěšÄÅ™}}
+{{prvnívelké:ěšÄÅ™}}
+{{PRVNÃMALÉ:ěšÄÅ™}}
+{{prvnímalé:ěšÄÅ™}}
+{{MALÃ:ěšÄÅ™}}
+{{malá:ěšÄÅ™}}
+{{VELKÃ:ěšÄÅ™}}
+{{velká:ěšÄÅ™}}
+!! result
+<p>ĚšÄÅ™
+ĚšÄÅ™
+ěšÄÅ™
+ěšÄÅ™
+ěšÄÅ™
+ěšÄÅ™
+ĚŠČŘ
+ĚŠČŘ
+</p>
+!! end
+
+!! test
+Morwen/13: Unclosed link followed by heading
+!! input
+[[link
+==heading==
+!! result
+<p>[[link
+</p>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: heading">edit</a>]</span> <span class="mw-headline" id="heading">heading</span></h2>
+
+!! end
+
+!! test
+HHP2.1: Heuristics for headings in preprocessor parenthetical structures
+!! input
+{{foo|
+=heading=
+!! result
+<p>{{foo|
+</p>
+<h1> <span class="mw-headline" id="heading">heading</span></h1>
+
+!! end
+
+!! test
+HHP2.2: Heuristics for headings in preprocessor parenthetical structures
+!! input
+{{foo|
+==heading==
+!! result
+<p>{{foo|
+</p>
+<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Edit section: heading">edit</a>]</span> <span class="mw-headline" id="heading">heading</span></h2>
+
+!! end
+
+!! test
+Tildes in comments
+!! options
+pst
+!! input
+<!-- ~~~~ -->
+!! result
+<!-- ~~~~ -->
+!! end
+
+!! test
+Paragraphs inside divs (no extra line breaks)
+!! input
+<div>Line one
+
+Line two</div>
+!! result
+<div>Line one
+Line two</div>
+
+!! end
+
+!! test
+Paragraphs inside divs (extra line break on open)
+!! input
+<div>
+Line one
+
+Line two</div>
+!! result
+<div>
+<p>Line one
+</p>
+Line two</div>
+
+!! end
+
+!! test
+Paragraphs inside divs (extra line break on close)
+!! input
+<div>Line one
+
+Line two
+</div>
+!! result
+<div>Line one
+<p>Line two
+</p>
+</div>
+
+!! end
+
+!! test
+Paragraphs inside divs (extra line break on open and close)
+!! input
+<div>
+Line one
+
+Line two
+</div>
+!! result
+<div>
+<p>Line one
+</p><p>Line two
+</p>
+</div>
+
+!! end
+
+!! test
+Nesting tags, paragraphs on lines which begin with <div>
+!! options
+disabled
+!! input
+<div></div><strong>A
+B</strong>
+!! result
+<div></div>
+<p><strong>A
+B</strong>
+</p>
+!! end
+
+# Bug 6200: <blockquote> should behave like <div> with respect to line breaks
+!! test
+Bug 6200: paragraphs inside blockquotes (no extra line breaks)
+!! options
+disabled
+!! input
+<blockquote>Line one
+
+Line two</blockquote>
+!! result
+<blockquote>Line one
+Line two</blockquote>
+
+!! end
+
+!! test
+Bug 6200: paragraphs inside blockquotes (extra line break on open)
+!! options
+disabled
+!! input
+<blockquote>
+Line one
+
+Line two</blockquote>
+!! result
+<blockquote>
+<p>Line one
+</p>
+Line two</blockquote>
+
+!! end
+
+!! test
+Bug 6200: paragraphs inside blockquotes (extra line break on close)
+!! options
+disabled
+!! input
+<blockquote>Line one
+
+Line two
+</blockquote>
+!! result
+<blockquote>Line one
+<p>Line two
+</p>
+</blockquote>
+
+!! end
+
+!! test
+Bug 6200: paragraphs inside blockquotes (extra line break on open and close)
+!! options
+disabled
+!! input
+<blockquote>
+Line one
+
+Line two
+</blockquote>
+!! result
+<blockquote>
+<p>Line one
+</p><p>Line two
+</p>
+</blockquote>
+
+!! end
+
+!! test
+Paragraphs inside blockquotes/divs (no extra line breaks)
+!! input
+<blockquote><div>Line one
+
+Line two</div></blockquote>
+!! result
+<blockquote><div>Line one
+Line two</div></blockquote>
+
+!! end
+
+!! test
+Paragraphs inside blockquotes/divs (extra line break on open)
+!! input
+<blockquote><div>
+Line one
+
+Line two</div></blockquote>
+!! result
+<blockquote><div>
+<p>Line one
+</p>
+Line two</div></blockquote>
+
+!! end
+
+!! test
+Paragraphs inside blockquotes/divs (extra line break on close)
+!! input
+<blockquote><div>Line one
+
+Line two
+</div></blockquote>
+!! result
+<blockquote><div>Line one
+<p>Line two
+</p>
+</div></blockquote>
+
+!! end
+
+!! test
+Paragraphs inside blockquotes/divs (extra line break on open and close)
+!! input
+<blockquote><div>
+Line one
+
+Line two
+</div></blockquote>
+!! result
+<blockquote><div>
+<p>Line one
+</p><p>Line two
+</p>
+</div></blockquote>
+
+!! end
+
+!! test
+Interwiki links trounced by replaceExternalLinks after early LinkHolderArray expansion
+!! options
+wgLinkHolderBatchSize=0
+!! input
+[[meatball:1]]
+[[meatball:2]]
+[[meatball:3]]
+!! result
+<p><a href="http://www.usemod.com/cgi-bin/mb.pl?1" class="extiw" title="meatball:1">meatball:1</a>
+<a href="http://www.usemod.com/cgi-bin/mb.pl?2" class="extiw" title="meatball:2">meatball:2</a>
+<a href="http://www.usemod.com/cgi-bin/mb.pl?3" class="extiw" title="meatball:3">meatball:3</a>
+</p>
+!! end
+
+!! test
+Free external link invading image caption
+!! input
+[[Image:Foobar.jpg|thumb|http://x|hello]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>hello</div></div></div>
+
+!! end
+
+!! test
+Bug 15196: localised external link numbers
+!! options
+language=fa
+!! input
+[http://en.wikipedia.org/]
+!! result
+<p><a rel="nofollow" class="external autonumber" href="http://en.wikipedia.org/">[Û±]</a>
+</p>
+!! end
+
+!! test
+Multibyte character in padleft
+!! input
+{{padleft:-Hello|7|Æ}}
+!! result
+<p>Æ-Hello
+</p>
+!! end
+
+!! test
+Multibyte character in padright
+!! input
+{{padright:Hello-|7|Æ}}
+!! result
+<p>Hello-Æ
+</p>
+!! end
+
+!! test
+Formatted date
+!! config
+wgUseDynamicDates=1
+!! input
+[[2009-03-24]]
+!! result
+<p><span class="mw-formatted-date" title="2009-03-24"><a href="/index.php?title=2009&amp;action=edit&amp;redlink=1" class="new" title="2009 (page does not exist)">2009</a>-<a href="/index.php?title=March_24&amp;action=edit&amp;redlink=1" class="new" title="March 24 (page does not exist)">03-24</a></span>
+</p>
+!!end
+
+!!test
+formatdate parser function
+!!input
+{{#formatdate:2009-03-24}}
+!! result
+<p><span class="mw-formatted-date" title="2009-03-24">2009-03-24</span>
+</p>
+!! end
+
+!!test
+formatdate parser function, with default format
+!!input
+{{#formatdate:2009-03-24|mdy}}
+!! result
+<p><span class="mw-formatted-date" title="2009-03-24">March 24, 2009</span>
+</p>
+!! end
+
+!! test
+Linked date with autoformatting disabled
+!! config
+wgUseDynamicDates=false
+!! input
+[[2009-03-24]]
+!! result
+<p><a href="/index.php?title=2009-03-24&amp;action=edit&amp;redlink=1" class="new" title="2009-03-24 (page does not exist)">2009-03-24</a>
+</p>
+!! end
+
+!! test
+Spacing of numbers in formatted dates
+!! input
+{{#formatdate:January 15}}
+!! result
+<p><span class="mw-formatted-date" title="01-15">January 15</span>
+</p>
+!! end
+
+!! test
+Spacing of numbers in formatted dates (linked)
+!! config
+wgUseDynamicDates=true
+!! input
+[[January 15]]
+!! result
+<p><span class="mw-formatted-date" title="01-15"><a href="/index.php?title=January_15&amp;action=edit&amp;redlink=1" class="new" title="January 15 (page does not exist)">January 15</a></span>
+</p>
+!! end
+
+#
+#
+#
+
+#
+# Edit comments
+#
+
+!! test
+Edit comment with link
+!! options
+comment
+!! input
+I like the [[Main Page]] a lot
+!! result
+I like the <a href="/wiki/Main_Page" title="Main Page">Main Page</a> a lot
+!!end
+
+!! test
+Edit comment with link and link text
+!! options
+comment
+!! input
+I like the [[Main Page|best pages]] a lot
+!! result
+I like the <a href="/wiki/Main_Page" title="Main Page">best pages</a> a lot
+!!end
+
+!! test
+Edit comment with link and link text with suffix
+!! options
+comment
+!! input
+I like the [[Main Page|best page]]s a lot
+!! result
+I like the <a href="/wiki/Main_Page" title="Main Page">best pages</a> a lot
+!!end
+
+!! test
+Edit comment with section link (non-local, eg in history list)
+!! options
+comment title=[[Main Page]]
+!! input
+/* External links */ removed bogus entries
+!! result
+<span class="autocomment"><a href="/wiki/Main_Page#External_links" title="Main Page">→</a>External links: </span> removed bogus entries
+!!end
+
+!! test
+Edit comment with section link (local, eg in diff view)
+!! options
+comment local title=[[Main Page]]
+!! input
+/* External links */ removed bogus entries
+!! result
+<span class="autocomment"><a href="#External_links">→</a>External links: </span> removed bogus entries
+!!end
+
+!! test
+Edit comment with subpage link (bug 14080)
+!! options
+comment
+subpage
+title=[[Subpage test]]
+!! input
+Poked at a [[/subpage]] here...
+!! result
+Poked at a <a href="/wiki/Subpage_test/subpage" title="Subpage test/subpage">/subpage</a> here...
+!!end
+
+!! test
+Edit comment with subpage link and link text (bug 14080)
+!! options
+comment
+subpage
+title=[[Subpage test]]
+!! input
+Poked at a [[/subpage|neat little page]] here...
+!! result
+Poked at a <a href="/wiki/Subpage_test/subpage" title="Subpage test/subpage">neat little page</a> here...
+!!end
+
+!! test
+Edit comment with bogus subpage link in non-subpage NS (bug 14080)
+!! options
+comment
+title=[[Subpage test]]
+!! input
+Poked at a [[/subpage]] here...
+!! result
+Poked at a <a href="/index.php?title=/subpage&amp;action=edit&amp;redlink=1" class="new" title="/subpage (page does not exist)">/subpage</a> here...
+!!end
+
+!! test
+Edit comment with bare anchor link (local, as on diff)
+!! options
+comment
+local
+title=[[Main Page]]
+!!input
+[[#section]]
+!! result
+<a href="#section">#section</a>
+!! end
+
+!! test
+Edit comment with bare anchor link (non-local, as on history)
+!! options
+comment
+title=[[Main Page]]
+!!input
+[[#section]]
+!! result
+<a href="/wiki/Main_Page#section" title="Main Page">#section</a>
+!! end
+
+!! test
+Anchor starting with underscore
+!!input
+[[#_ref|One]]
+!! result
+<p><a href="#_ref">One</a>
+</p>
+!! end
+
+!! test
+Id starting with underscore
+!!input
+<div id="_ref"></div>
+!! result
+<div id="_ref"></div>
+
+!! end
+
+!! test
+Space normalisation on autocomment (bug 22784)
+!! options
+comment
+title=[[Main Page]]
+!!input
+/* __hello__world__ */
+!! result
+<span class="autocomment"><a href="/wiki/Main_Page#hello_world" title="Main Page">→</a>__hello__world__</span>
+!! end
+
+!! test
+percent-encoding and + signs in comments (Bug 26410)
+!! options
+comment
+!!input
+[[ABC%33D% ++]] [[ABC%33D% ++|+%20]]
+!! result
+<a href="/index.php?title=ABC3D%25_%2B%2B&amp;action=edit&amp;redlink=1" class="new" title="ABC3D% ++ (page does not exist)">ABC3D% ++</a> <a href="/index.php?title=ABC3D%25_%2B%2B&amp;action=edit&amp;redlink=1" class="new" title="ABC3D% ++ (page does not exist)">+%20</a>
+!! end
+
+!! test
+Bad images - basic functionality
+!! input
+[[File:Bad.jpg]]
+!! result
+!! end
+
+!! test
+Bad images - bug 16039: text after bad image disappears
+!! input
+Foo bar
+[[File:Bad.jpg]]
+Bar foo
+!! result
+<p>Foo bar
+</p><p>Bar foo
+</p>
+!! end
+
+!! test
+Verify that displaytitle works (bug #22501) no displaytitle
+!! options
+showtitle
+!! config
+wgAllowDisplayTitle=true
+wgRestrictDisplayTitle=false
+!! input
+this is not the the title
+!! result
+Parser test
+<p>this is not the the title
+</p>
+!! end
+
+!! test
+Verify that displaytitle works (bug #22501) RestrictDisplayTitle=false
+!! options
+showtitle
+title=[[Screen]]
+!! config
+wgAllowDisplayTitle=true
+wgRestrictDisplayTitle=false
+!! input
+this is not the the title
+{{DISPLAYTITLE:whatever}}
+!! result
+whatever
+<p>this is not the the title
+</p>
+!! end
+
+!! test
+Verify that displaytitle works (bug #22501) RestrictDisplayTitle=true mismatch
+!! options
+showtitle
+title=[[Screen]]
+!! config
+wgAllowDisplayTitle=true
+wgRestrictDisplayTitle=true
+!! input
+this is not the the title
+{{DISPLAYTITLE:whatever}}
+!! result
+Screen
+<p>this is not the the title
+</p>
+!! end
+
+!! test
+Verify that displaytitle works (bug #22501) RestrictDisplayTitle=true matching
+!! options
+showtitle
+title=[[Screen]]
+!! config
+wgAllowDisplayTitle=true
+wgRestrictDisplayTitle=true
+!! input
+this is not the the title
+{{DISPLAYTITLE:screen}}
+!! result
+screen
+<p>this is not the the title
+</p>
+!! end
+
+!! test
+Verify that displaytitle works (bug #22501) AllowDisplayTitle=false
+!! options
+showtitle
+title=[[Screen]]
+!! config
+wgAllowDisplayTitle=false
+!! input
+this is not the the title
+{{DISPLAYTITLE:screen}}
+!! result
+Screen
+<p>this is not the the title
+<a href="/index.php?title=Template:DISPLAYTITLE:screen&amp;action=edit&amp;redlink=1" class="new" title="Template:DISPLAYTITLE:screen (page does not exist)">Template:DISPLAYTITLE:screen</a>
+</p>
+!! end
+
+!! test
+Verify that displaytitle works (bug #22501) AllowDisplayTitle=false no DISPLAYTITLE
+!! options
+showtitle
+title=[[Screen]]
+!! config
+wgAllowDisplayTitle=false
+!! input
+this is not the the title
+!! result
+Screen
+<p>this is not the the title
+</p>
+!! end
+
+!! test
+preload: check <noinclude> and <includeonly>
+!! options
+preload
+!! input
+Hello <noinclude>cruel</noinclude><includeonly>kind</includeonly> world.
+!! result
+Hello kind world.
+!! end
+
+!! test
+preload: check <onlyinclude>
+!! options
+preload
+!! input
+Goodbye <onlyinclude>Hello world</onlyinclude>
+!! result
+Hello world
+!! end
+
+!! test
+preload: can pass tags through if we want to
+!! options
+preload
+!! input
+<includeonly><</includeonly>includeonly>Hello world<includeonly><</includeonly>/includeonly>
+!! result
+<includeonly>Hello world</includeonly>
+!! end
+
+!! test
+preload: check that it doesn't try to do tricks
+!! options
+preload
+!! input
+* <!-- Hello --> ''{{world}}'' {{<includeonly>subst:</includeonly>How are you}}{{ {{{|safesubst:}}} #if:1|2|3}}
+!! result
+* <!-- Hello --> ''{{world}}'' {{subst:How are you}}{{ {{{|safesubst:}}} #if:1|2|3}}
+!! end
+
+!! test
+Play a bit with r67090 and bug 3158
+!! options
+disabled
+!! input
+<div style="width:50% !important">&nbsp;</div>
+<div style="width:50%&nbsp;!important">&nbsp;</div>
+<div style="width:50%&#160;!important">&nbsp;</div>
+<div style="border : solid;">&nbsp;</div>
+!! result
+<div style="width:50% !important">&nbsp;</div>
+<div style="width:50% !important">&nbsp;</div>
+<div style="width:50% !important">&nbsp;</div>
+<div style="border&#160;: solid;">&nbsp;</div>
+
+!! end
+
+!! test
+HTML5 data attributes
+!! input
+<span data-foo="bar">Baz</span>
+<p data-abc-def_hij="">Quuz</p>
+!! result
+<p><span data-foo="bar">Baz</span>
+</p>
+<p data-abc-def_hij="">Quuz</p>
+
+!! end
+
+!! test
+percent-encoding and + signs in internal links (Bug 26410)
+!! input
+[[User:+%]] [[Page+title%]]
+[[%+]] [[%+|%20]] [[%+ ]] [[%+r]]
+[[%]] [[+]] [[image:%+abc%39|foo|[[bar]]]]
+[[%33%45]] [[%33%45+]]
+!! result
+<p><a href="/index.php?title=User:%2B%25&amp;action=edit&amp;redlink=1" class="new" title="User:+% (page does not exist)">User:+%</a> <a href="/index.php?title=Page%2Btitle%25&amp;action=edit&amp;redlink=1" class="new" title="Page+title% (page does not exist)">Page+title%</a>
+<a href="/index.php?title=%25%2B&amp;action=edit&amp;redlink=1" class="new" title="%+ (page does not exist)">%+</a> <a href="/index.php?title=%25%2B&amp;action=edit&amp;redlink=1" class="new" title="%+ (page does not exist)">%20</a> <a href="/index.php?title=%25%2B&amp;action=edit&amp;redlink=1" class="new" title="%+ (page does not exist)">%+ </a> <a href="/index.php?title=%25%2Br&amp;action=edit&amp;redlink=1" class="new" title="%+r (page does not exist)">%+r</a>
+<a href="/index.php?title=%25&amp;action=edit&amp;redlink=1" class="new" title="% (page does not exist)">%</a> <a href="/index.php?title=%2B&amp;action=edit&amp;redlink=1" class="new" title="+ (page does not exist)">+</a> <a href="/index.php?title=Special:Upload&amp;wpDestFile=%25%2Babc9" class="new" title="File:%+abc9">bar</a>
+<a href="/index.php?title=3E&amp;action=edit&amp;redlink=1" class="new" title="3E (page does not exist)">3E</a> <a href="/index.php?title=3E%2B&amp;action=edit&amp;redlink=1" class="new" title="3E+ (page does not exist)">3E+</a>
+</p>
+!! end
+
+!! test
+Special characters in embedded file links (bug 27679)
+!! input
+[[File:Contains & ampersand.jpg]]
+[[File:Does not exist.jpg|Title with & ampersand]]
+!! result
+<p><a href="/index.php?title=Special:Upload&amp;wpDestFile=Contains_%26_ampersand.jpg" class="new" title="File:Contains &amp; ampersand.jpg">File:Contains &amp; ampersand.jpg</a>
+<a href="/index.php?title=Special:Upload&amp;wpDestFile=Does_not_exist.jpg" class="new" title="File:Does not exist.jpg">Title with &amp; ampersand</a>
+</p>
+!! end
+
+
+!! test
+Confirm that 'apos' named character reference doesn't make it to output (not legal in HTML 4)
+!! input
+Text&apos;s been normalized?
+!! result
+<p>Text&#39;s been normalized?
+</p>
+!! end
+
+!! article
+Mediawiki:loop1
+!! text
+{{Identical|A}}
+!! endarticle
+
+!! article
+Mediawiki:loop2
+!! text
+{{Identical|B}}
+!! endarticle
+
+!! article
+Template:Identical
+!! text
+{{int:loop1}}
+{{int:loop2}}
+!! endarticle
+
+!! test
+Bug 31098 Template which includes system messages which includes the template
+!! input
+{{Identical}}
+!! result
+<p><span class="error">Template loop detected: <a href="/wiki/Template:Identical" title="Template:Identical">Template:Identical</a></span>
+<span class="error">Template loop detected: <a href="/wiki/Template:Identical" title="Template:Identical">Template:Identical</a></span>
+</p>
+!! end
+
+!! test
+Bug31490 Turkish: ucfirst 'blah'
+!! options
+language=tr
+!! input
+{{ucfirst:blah}}
+!! result
+<p>Blah
+</p>
+!! end
+
+!! test
+Bug31490 Turkish: ucfirst 'ix'
+!! options
+language=tr
+!! input
+{{ucfirst:ix}}
+!! result
+<p>Ä°x
+</p>
+!! end
+
+!! test
+Bug31490 Turkish: lcfirst 'BLAH'
+!! options
+language=tr
+!! input
+{{lcfirst:BLAH}}
+!! result
+<p>bLAH
+</p>
+!! end
+
+!! test
+Bug31490 Turkish: ucfırst (with a dotless i)
+!! options
+language=tr
+!! input
+{{ucfırst:blah}}
+!! result
+<p><a href="/index.php?title=%C5%9Eablon:Ucf%C4%B1rst:blah&amp;action=edit&amp;redlink=1" class="new" title="Şablon:Ucfırst:blah (sayfa mevcut değil)">Şablon:Ucfırst:blah</a>
+</p>
+!! end
+
+!! test
+Bug31490 ucfırst (with a dotless i) with English language
+!! options
+language=en
+!! input
+{{ucfırst:blah}}
+!! result
+<p><a href="/index.php?title=Template:Ucf%C4%B1rst:blah&amp;action=edit&amp;redlink=1" class="new" title="Template:Ucfırst:blah (page does not exist)">Template:Ucfırst:blah</a>
+</p>
+!! end
+
+
+!! article
+MediaWiki:Bug32057
+!! text
+== {{int:headline_sample}} ==
+!! endarticle
+
+!! test
+Bug 32057: Title needed when expanding <h> nodes.
+!! options
+title=[[Main Page]]
+!! input
+{{int:Bug32057}}
+!! result
+<h2><span class="editsection">[<a href="/index.php?title=Main_Page&amp;action=edit&amp;section=1" title="Edit section: Headline text">edit</a>]</span> <span class="mw-headline" id="Headline_text"> Headline text </span></h2>
+
+!! end
+
+TODO:
+more images
+more tables
+math
+character entities
+and much more
+Try for 100% code coverage
diff --git a/tests/parser/parserTestsParserHook.php b/tests/parser/parserTestsParserHook.php
new file mode 100644
index 00000000..324b8e5c
--- /dev/null
+++ b/tests/parser/parserTestsParserHook.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * A basic extension that's used by the parser tests to test whether input and
+ * arguments are passed to extensions properly.
+ *
+ * Copyright © 2005, 2006 Ævar Arnfjörð Bjarmason
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Testing
+ * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ */
+
+class ParserTestParserHook {
+
+ static function setup( &$parser ) {
+ $parser->setHook( 'tag', array( __CLASS__, 'dumpHook' ) );
+
+ return true;
+ }
+
+ static function dumpHook( $in, $argv ) {
+ ob_start();
+ var_dump(
+ $in,
+ $argv
+ );
+ $ret = ob_get_clean();
+
+ return "<pre>\n$ret</pre>";
+ }
+}
diff --git a/tests/parser/parserTestsStaticParserHook.php b/tests/parser/parserTestsStaticParserHook.php
new file mode 100644
index 00000000..e82f7f3f
--- /dev/null
+++ b/tests/parser/parserTestsStaticParserHook.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * A basic extension that's used by the parser tests to test whether the parser
+ * calls extensions when they're called inside comments, it shouldn't do that
+ *
+ * Copyright © 2005, 2006 Ævar Arnfjörð Bjarmason
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Testing
+ * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ */
+
+class ParserTestStaticParserHook {
+ static function setup( &$parser ) {
+ $parser->setHook( 'statictag', array( __CLASS__, 'hook' ) );
+
+ return true;
+ }
+
+ static function hook( $in, $argv, $parser ) {
+ if ( ! count( $argv ) ) {
+ $parser->static_tag_buf = $in;
+ return '';
+ } elseif ( count( $argv ) === 1 && isset( $argv['action'] )
+ && $argv['action'] === 'flush' && $in === null )
+ {
+ // Clear the buffer, we probably don't need to
+ if ( isset( $parser->static_tag_buf ) ) {
+ $tmp = $parser->static_tag_buf;
+ } else {
+ $tmp = '';
+ }
+ $parser->static_tag_buf = null;
+ return $tmp;
+ } else
+ // wtf?
+ return
+ "\nCall this extension as <statictag>string</statictag> or as" .
+ " <statictag action=flush/>, not in any other way.\n" .
+ "text: " . var_export( $in, true ) . "\n" .
+ "argv: " . var_export( $argv, true ) . "\n";
+ }
+}
diff --git a/tests/parser/preprocess/All_system_messages.expected b/tests/parser/preprocess/All_system_messages.expected
new file mode 100644
index 00000000..96d4569b
--- /dev/null
+++ b/tests/parser/preprocess/All_system_messages.expected
@@ -0,0 +1,5646 @@
+<root><template lineStart="1"><title>int:allmessagestext</title></template>
+
+&lt;table border=1 width=100%&gt;&lt;tr&gt;&lt;td&gt;
+'''Name'''
+&lt;/td&gt;&lt;td&gt;
+'''Default text'''
+&lt;/td&gt;&lt;td&gt;
+'''Current text'''
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:1movedto2&amp;action=edit 1movedto2]&lt;br&gt;
+[[MediaWiki_talk:1movedto2|Talk]]
+&lt;/td&gt;&lt;td&gt;
+$1 moved to $2
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:1movedto2</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Monobook.css&amp;action=edit Monobook.css]&lt;br&gt;
+[[MediaWiki_talk:Monobook.css|Talk]]
+&lt;/td&gt;&lt;td&gt;
+/* edit this file to customize the monobook skin for the entire site */
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Monobook.css</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:About&amp;action=edit about]&lt;br&gt;
+[[MediaWiki_talk:About|Talk]]
+&lt;/td&gt;&lt;td&gt;
+About
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:About</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Aboutpage&amp;action=edit aboutpage]&lt;br&gt;
+[[MediaWiki_talk:Aboutpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary:About
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Aboutpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Aboutwikipedia&amp;action=edit aboutwikipedia]&lt;br&gt;
+[[MediaWiki_talk:Aboutwikipedia|Talk]]
+&lt;/td&gt;&lt;td&gt;
+About Wiktionary
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Aboutwikipedia</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-addsection&amp;action=edit accesskey-addsection]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-addsection|Talk]]
+&lt;/td&gt;&lt;td&gt;
++
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-addsection</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-anontalk&amp;action=edit accesskey-anontalk]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-anontalk|Talk]]
+&lt;/td&gt;&lt;td&gt;
+n
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-anontalk</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-anonuserpage&amp;action=edit accesskey-anonuserpage]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-anonuserpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-anonuserpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-article&amp;action=edit accesskey-article]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-article|Talk]]
+&lt;/td&gt;&lt;td&gt;
+a
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-article</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-compareselectedversions&amp;action=edit accesskey-compareselectedversions]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-compareselectedversions|Talk]]
+&lt;/td&gt;&lt;td&gt;
+v
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-compareselectedversions</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-contributions&amp;action=edit accesskey-contributions]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-contributions|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;amp;lt;accesskey-contributions&amp;amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-contributions</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-currentevents&amp;action=edit accesskey-currentevents]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-currentevents|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;amp;lt;accesskey-currentevents&amp;amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-currentevents</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-delete&amp;action=edit accesskey-delete]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-delete|Talk]]
+&lt;/td&gt;&lt;td&gt;
+d
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-delete</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-edit&amp;action=edit accesskey-edit]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-edit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+e
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-edit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-emailuser&amp;action=edit accesskey-emailuser]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-emailuser|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;amp;lt;accesskey-emailuser&amp;amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-emailuser</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-help&amp;action=edit accesskey-help]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-help|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;amp;lt;accesskey-help&amp;amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-help</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-history&amp;action=edit accesskey-history]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-history|Talk]]
+&lt;/td&gt;&lt;td&gt;
+h
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-history</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-login&amp;action=edit accesskey-login]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-login|Talk]]
+&lt;/td&gt;&lt;td&gt;
+o
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-login</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-logout&amp;action=edit accesskey-logout]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-logout|Talk]]
+&lt;/td&gt;&lt;td&gt;
+o
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-logout</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-mainpage&amp;action=edit accesskey-mainpage]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-mainpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+z
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-mainpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-minoredit&amp;action=edit accesskey-minoredit]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-minoredit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+i
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-minoredit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-move&amp;action=edit accesskey-move]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-move|Talk]]
+&lt;/td&gt;&lt;td&gt;
+m
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-move</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-mycontris&amp;action=edit accesskey-mycontris]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-mycontris|Talk]]
+&lt;/td&gt;&lt;td&gt;
+y
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-mycontris</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-mytalk&amp;action=edit accesskey-mytalk]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-mytalk|Talk]]
+&lt;/td&gt;&lt;td&gt;
+n
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-mytalk</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-portal&amp;action=edit accesskey-portal]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-portal|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;amp;lt;accesskey-portal&amp;amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-portal</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-preferences&amp;action=edit accesskey-preferences]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-preferences|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;amp;lt;accesskey-preferences&amp;amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-preferences</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-preview&amp;action=edit accesskey-preview]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-preview|Talk]]
+&lt;/td&gt;&lt;td&gt;
+p
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-preview</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-protect&amp;action=edit accesskey-protect]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-protect|Talk]]
+&lt;/td&gt;&lt;td&gt;
+=
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-protect</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-randompage&amp;action=edit accesskey-randompage]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-randompage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+x
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-randompage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-recentchanges&amp;action=edit accesskey-recentchanges]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-recentchanges|Talk]]
+&lt;/td&gt;&lt;td&gt;
+r
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-recentchanges</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-recentchangeslinked&amp;action=edit accesskey-recentchangeslinked]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-recentchangeslinked|Talk]]
+&lt;/td&gt;&lt;td&gt;
+c
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-recentchangeslinked</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-save&amp;action=edit accesskey-save]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-save|Talk]]
+&lt;/td&gt;&lt;td&gt;
+s
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-save</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-search&amp;action=edit accesskey-search]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-search|Talk]]
+&lt;/td&gt;&lt;td&gt;
+f
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-search</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-sitesupport&amp;action=edit accesskey-sitesupport]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-sitesupport|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;amp;lt;accesskey-sitesupport&amp;amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-sitesupport</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-specialpage&amp;action=edit accesskey-specialpage]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-specialpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;amp;lt;accesskey-specialpage&amp;amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-specialpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-specialpages&amp;action=edit accesskey-specialpages]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-specialpages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+q
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-specialpages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-talk&amp;action=edit accesskey-talk]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-talk|Talk]]
+&lt;/td&gt;&lt;td&gt;
+t
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-talk</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-undelete&amp;action=edit accesskey-undelete]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-undelete|Talk]]
+&lt;/td&gt;&lt;td&gt;
+d
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-undelete</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-unwatch&amp;action=edit accesskey-unwatch]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-unwatch|Talk]]
+&lt;/td&gt;&lt;td&gt;
+w
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-unwatch</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-upload&amp;action=edit accesskey-upload]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-upload|Talk]]
+&lt;/td&gt;&lt;td&gt;
+u
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-upload</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-userpage&amp;action=edit accesskey-userpage]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-userpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-userpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-viewsource&amp;action=edit accesskey-viewsource]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-viewsource|Talk]]
+&lt;/td&gt;&lt;td&gt;
+e
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-viewsource</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-watch&amp;action=edit accesskey-watch]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-watch|Talk]]
+&lt;/td&gt;&lt;td&gt;
+w
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-watch</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-watchlist&amp;action=edit accesskey-watchlist]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-watchlist|Talk]]
+&lt;/td&gt;&lt;td&gt;
+l
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-watchlist</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-whatlinkshere&amp;action=edit accesskey-whatlinkshere]&lt;br&gt;
+[[MediaWiki_talk:Accesskey-whatlinkshere|Talk]]
+&lt;/td&gt;&lt;td&gt;
+b
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accesskey-whatlinkshere</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accmailtext&amp;action=edit accmailtext]&lt;br&gt;
+[[MediaWiki_talk:Accmailtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The Password for &amp;#39;$1&amp;#39; has been sent to $2.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accmailtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accmailtitle&amp;action=edit accmailtitle]&lt;br&gt;
+[[MediaWiki_talk:Accmailtitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Password sent.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Accmailtitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Actioncomplete&amp;action=edit actioncomplete]&lt;br&gt;
+[[MediaWiki_talk:Actioncomplete|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Action complete
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Actioncomplete</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Addedwatch&amp;action=edit addedwatch]&lt;br&gt;
+[[MediaWiki_talk:Addedwatch|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Added to watchlist
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Addedwatch</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Addedwatchtext&amp;action=edit addedwatchtext]&lt;br&gt;
+[[MediaWiki_talk:Addedwatchtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The page &amp;quot;$1&amp;quot; has been added to your &amp;#91;&amp;#91;Special:Watchlist&amp;#124;watchlist]].
+Future changes to this page and its associated Talk page will be listed there,
+and the page will appear &amp;#39;&amp;#39;&amp;#39;bolded&amp;#39;&amp;#39;&amp;#39; in the &amp;#91;&amp;#91;Special:Recentchanges&amp;#124;list of recent changes]] to
+make it easier to pick out.
+
+&amp;lt;p&amp;gt;If you want to remove the page from your watchlist later, click &amp;quot;Stop watching&amp;quot; in the sidebar.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Addedwatchtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Addsection&amp;action=edit addsection]&lt;br&gt;
+[[MediaWiki_talk:Addsection|Talk]]
+&lt;/td&gt;&lt;td&gt;
++
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Addsection</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Administrators&amp;action=edit administrators]&lt;br&gt;
+[[MediaWiki_talk:Administrators|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary:Administrators
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Administrators</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Affirmation&amp;action=edit affirmation]&lt;br&gt;
+[[MediaWiki_talk:Affirmation|Talk]]
+&lt;/td&gt;&lt;td&gt;
+I affirm that the copyright holder of this file
+agrees to license it under the terms of the $1.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Affirmation</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:All&amp;action=edit all]&lt;br&gt;
+[[MediaWiki_talk:All|Talk]]
+&lt;/td&gt;&lt;td&gt;
+all
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:All</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Allmessages&amp;action=edit allmessages]&lt;br&gt;
+[[MediaWiki_talk:Allmessages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+All system messages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Allmessages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Allmessagestext&amp;action=edit allmessagestext]&lt;br&gt;
+[[MediaWiki_talk:Allmessagestext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This is a list of all system messages available in the MediaWiki: namespace.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Allmessagestext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Allpages&amp;action=edit allpages]&lt;br&gt;
+[[MediaWiki_talk:Allpages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+All pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Allpages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Alphaindexline&amp;action=edit alphaindexline]&lt;br&gt;
+[[MediaWiki_talk:Alphaindexline|Talk]]
+&lt;/td&gt;&lt;td&gt;
+$1 to $2
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Alphaindexline</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Alreadyloggedin&amp;action=edit alreadyloggedin]&lt;br&gt;
+[[MediaWiki_talk:Alreadyloggedin|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;font color=red&amp;gt;&amp;lt;b&amp;gt;User $1, you are already logged in!&amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt;&amp;lt;br /&amp;gt;
+
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Alreadyloggedin</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Alreadyrolled&amp;action=edit alreadyrolled]&lt;br&gt;
+[[MediaWiki_talk:Alreadyrolled|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Cannot rollback last edit of &amp;#91;&amp;#91;$1]]
+by &amp;#91;&amp;#91;User:$2&amp;#124;$2]] (&amp;#91;&amp;#91;User talk:$2&amp;#124;Talk]]); someone else has edited or rolled back the page already.
+
+Last edit was by &amp;#91;&amp;#91;User:$3&amp;#124;$3]] (&amp;#91;&amp;#91;User talk:$3&amp;#124;Talk]]).
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Alreadyrolled</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ancientpages&amp;action=edit ancientpages]&lt;br&gt;
+[[MediaWiki_talk:Ancientpages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Oldest pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ancientpages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:And&amp;action=edit and]&lt;br&gt;
+[[MediaWiki_talk:And|Talk]]
+&lt;/td&gt;&lt;td&gt;
+and
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:And</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Anontalk&amp;action=edit anontalk]&lt;br&gt;
+[[MediaWiki_talk:Anontalk|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Talk for this IP
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Anontalk</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Anontalkpagetext&amp;action=edit anontalkpagetext]&lt;br&gt;
+[[MediaWiki_talk:Anontalkpagetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+----&amp;#39;&amp;#39;This is the discussion page for an anonymous user who has not created an account yet or who does not use it. We therefore have to use the numerical &amp;#91;&amp;#91;IP address]] to identify him/her. Such an IP address can be shared by several users. If you are an anonymous user and feel that irrelevant comments have been directed at you, please &amp;#91;&amp;#91;Special:Userlogin&amp;#124;create an account or log in]] to avoid future confusion with other anonymous users.&amp;#39;&amp;#39;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Anontalkpagetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Anonymous&amp;action=edit anonymous]&lt;br&gt;
+[[MediaWiki_talk:Anonymous|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Anonymous user(s) of Wiktionary
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Anonymous</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Article&amp;action=edit article]&lt;br&gt;
+[[MediaWiki_talk:Article|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Content page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Article</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Articleexists&amp;action=edit articleexists]&lt;br&gt;
+[[MediaWiki_talk:Articleexists|Talk]]
+&lt;/td&gt;&lt;td&gt;
+A page of that name already exists, or the
+name you have chosen is not valid.
+Please choose another name.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Articleexists</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Articlepage&amp;action=edit articlepage]&lt;br&gt;
+[[MediaWiki_talk:Articlepage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View content page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Articlepage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Asksql&amp;action=edit asksql]&lt;br&gt;
+[[MediaWiki_talk:Asksql|Talk]]
+&lt;/td&gt;&lt;td&gt;
+SQL query
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Asksql</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Asksqltext&amp;action=edit asksqltext]&lt;br&gt;
+[[MediaWiki_talk:Asksqltext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Use the form below to make a direct query of the
+database.
+Use single quotes (&amp;#39;like this&amp;#39;) to delimit string literals.
+This can often add considerable load to the server, so please use
+this function sparingly.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Asksqltext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Autoblocker&amp;action=edit autoblocker]&lt;br&gt;
+[[MediaWiki_talk:Autoblocker|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Autoblocked because you share an IP address with &amp;quot;$1&amp;quot;. Reason &amp;quot;$2&amp;quot;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Autoblocker</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badarticleerror&amp;action=edit badarticleerror]&lt;br&gt;
+[[MediaWiki_talk:Badarticleerror|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This action cannot be performed on this page.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Badarticleerror</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badfilename&amp;action=edit badfilename]&lt;br&gt;
+[[MediaWiki_talk:Badfilename|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Image name has been changed to &amp;quot;$1&amp;quot;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Badfilename</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badfiletype&amp;action=edit badfiletype]&lt;br&gt;
+[[MediaWiki_talk:Badfiletype|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;quot;.$1&amp;quot; is not a recommended image file format.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Badfiletype</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badipaddress&amp;action=edit badipaddress]&lt;br&gt;
+[[MediaWiki_talk:Badipaddress|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Invalid IP address
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Badipaddress</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badquery&amp;action=edit badquery]&lt;br&gt;
+[[MediaWiki_talk:Badquery|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Badly formed search query
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Badquery</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badquerytext&amp;action=edit badquerytext]&lt;br&gt;
+[[MediaWiki_talk:Badquerytext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+We could not process your query.
+This is probably because you have attempted to search for a
+word fewer than three letters long, which is not yet supported.
+It could also be that you have mistyped the expression, for
+example &amp;quot;fish and and scales&amp;quot;.
+Please try another query.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Badquerytext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badretype&amp;action=edit badretype]&lt;br&gt;
+[[MediaWiki_talk:Badretype|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The passwords you entered do not match.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Badretype</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badtitle&amp;action=edit badtitle]&lt;br&gt;
+[[MediaWiki_talk:Badtitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Bad title
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Badtitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badtitletext&amp;action=edit badtitletext]&lt;br&gt;
+[[MediaWiki_talk:Badtitletext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The requested page title was invalid, empty, or
+an incorrectly linked inter-language or inter-wiki title.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Badtitletext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blanknamespace&amp;action=edit blanknamespace]&lt;br&gt;
+[[MediaWiki_talk:Blanknamespace|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(Main)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Blanknamespace</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blockedtext&amp;action=edit blockedtext]&lt;br&gt;
+[[MediaWiki_talk:Blockedtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Your user name or IP address has been blocked by $1.
+The reason given is this:&amp;lt;br /&amp;gt;&amp;#39;&amp;#39;$2&amp;#39;&amp;#39;&amp;lt;p&amp;gt;You may contact $1 or one of the other
+&amp;#91;&amp;#91;Wiktionary:Administrators&amp;#124;administrators]] to discuss the block.
+
+Note that you may not use the &amp;quot;email this user&amp;quot; feature unless you have a valid email address registered in your &amp;#91;&amp;#91;Special:Preferences&amp;#124;user preferences]].
+
+Your IP address is $3. Please include this address in any queries you make.
+
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Blockedtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blockedtitle&amp;action=edit blockedtitle]&lt;br&gt;
+[[MediaWiki_talk:Blockedtitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+User is blocked
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Blockedtitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blockip&amp;action=edit blockip]&lt;br&gt;
+[[MediaWiki_talk:Blockip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Block user
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Blockip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blockipsuccesssub&amp;action=edit blockipsuccesssub]&lt;br&gt;
+[[MediaWiki_talk:Blockipsuccesssub|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Block succeeded
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Blockipsuccesssub</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blockipsuccesstext&amp;action=edit blockipsuccesstext]&lt;br&gt;
+[[MediaWiki_talk:Blockipsuccesstext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;quot;$1&amp;quot; has been blocked.
+&amp;lt;br /&amp;gt;See &amp;#91;&amp;#91;Special:Ipblocklist&amp;#124;IP block list]] to review blocks.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Blockipsuccesstext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blockiptext&amp;action=edit blockiptext]&lt;br&gt;
+[[MediaWiki_talk:Blockiptext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Use the form below to block write access
+from a specific IP address or username.
+This should be done only only to prevent vandalism, and in
+accordance with &amp;#91;&amp;#91;Wiktionary:Policy&amp;#124;policy]].
+Fill in a specific reason below (for example, citing particular
+pages that were vandalized).
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Blockiptext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blocklink&amp;action=edit blocklink]&lt;br&gt;
+[[MediaWiki_talk:Blocklink|Talk]]
+&lt;/td&gt;&lt;td&gt;
+block
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Blocklink</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blocklistline&amp;action=edit blocklistline]&lt;br&gt;
+[[MediaWiki_talk:Blocklistline|Talk]]
+&lt;/td&gt;&lt;td&gt;
+$1, $2 blocked $3 (expires $4)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Blocklistline</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blocklogentry&amp;action=edit blocklogentry]&lt;br&gt;
+[[MediaWiki_talk:Blocklogentry|Talk]]
+&lt;/td&gt;&lt;td&gt;
+blocked &amp;quot;$1&amp;quot; with an expiry time of $2
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Blocklogentry</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blocklogpage&amp;action=edit blocklogpage]&lt;br&gt;
+[[MediaWiki_talk:Blocklogpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Block_log
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Blocklogpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blocklogtext&amp;action=edit blocklogtext]&lt;br&gt;
+[[MediaWiki_talk:Blocklogtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This is a log of user blocking and unblocking actions. Automatically
+blocked IP addresses are not be listed. See the &amp;#91;&amp;#91;Special:Ipblocklist&amp;#124;IP block list]] for
+the list of currently operational bans and blocks.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Blocklogtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bold_sample&amp;action=edit bold_sample]&lt;br&gt;
+[[MediaWiki_talk:Bold_sample|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Bold text
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Bold_sample</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bold_tip&amp;action=edit bold_tip]&lt;br&gt;
+[[MediaWiki_talk:Bold_tip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Bold text
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Bold_tip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Booksources&amp;action=edit booksources]&lt;br&gt;
+[[MediaWiki_talk:Booksources|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Book sources
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Booksources</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Booksourcetext&amp;action=edit booksourcetext]&lt;br&gt;
+[[MediaWiki_talk:Booksourcetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Below is a list of links to other sites that
+sell new and used books, and may also have further information
+about books you are looking for.Wiktionary is not affiliated with any of these businesses, and
+this list should not be construed as an endorsement.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Booksourcetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Brokenredirects&amp;action=edit brokenredirects]&lt;br&gt;
+[[MediaWiki_talk:Brokenredirects|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Broken Redirects
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Brokenredirects</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Brokenredirectstext&amp;action=edit brokenredirectstext]&lt;br&gt;
+[[MediaWiki_talk:Brokenredirectstext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The following redirects link to a non-existing pages.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Brokenredirectstext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bugreports&amp;action=edit bugreports]&lt;br&gt;
+[[MediaWiki_talk:Bugreports|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Bug reports
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Bugreports</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bugreportspage&amp;action=edit bugreportspage]&lt;br&gt;
+[[MediaWiki_talk:Bugreportspage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary:Bug_reports
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Bugreportspage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bureaucratlog&amp;action=edit bureaucratlog]&lt;br&gt;
+[[MediaWiki_talk:Bureaucratlog|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Bureaucrat_log
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Bureaucratlog</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bureaucratlogentry&amp;action=edit bureaucratlogentry]&lt;br&gt;
+[[MediaWiki_talk:Bureaucratlogentry|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Rights for user &amp;quot;$1&amp;quot; set &amp;quot;$2&amp;quot;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Bureaucratlogentry</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bureaucrattext&amp;action=edit bureaucrattext]&lt;br&gt;
+[[MediaWiki_talk:Bureaucrattext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The action you have requested can only be
+performed by sysops with &amp;quot;bureaucrat&amp;quot; status.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Bureaucrattext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bureaucrattitle&amp;action=edit bureaucrattitle]&lt;br&gt;
+[[MediaWiki_talk:Bureaucrattitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Bureaucrat access required
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Bureaucrattitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bydate&amp;action=edit bydate]&lt;br&gt;
+[[MediaWiki_talk:Bydate|Talk]]
+&lt;/td&gt;&lt;td&gt;
+by date
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Bydate</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Byname&amp;action=edit byname]&lt;br&gt;
+[[MediaWiki_talk:Byname|Talk]]
+&lt;/td&gt;&lt;td&gt;
+by name
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Byname</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bysize&amp;action=edit bysize]&lt;br&gt;
+[[MediaWiki_talk:Bysize|Talk]]
+&lt;/td&gt;&lt;td&gt;
+by size
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Bysize</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Cachederror&amp;action=edit cachederror]&lt;br&gt;
+[[MediaWiki_talk:Cachederror|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The following is a cached copy of the requested page, and may not be up to date.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Cachederror</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Cancel&amp;action=edit cancel]&lt;br&gt;
+[[MediaWiki_talk:Cancel|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Cancel
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Cancel</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Cannotdelete&amp;action=edit cannotdelete]&lt;br&gt;
+[[MediaWiki_talk:Cannotdelete|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Could not delete the page or image specified. (It may have already been deleted by someone else.)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Cannotdelete</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Cantrollback&amp;action=edit cantrollback]&lt;br&gt;
+[[MediaWiki_talk:Cantrollback|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Cannot revert edit; last contributor is only author of this page.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Cantrollback</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Categories&amp;action=edit categories]&lt;br&gt;
+[[MediaWiki_talk:Categories|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Categories
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Categories</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Category&amp;action=edit category]&lt;br&gt;
+[[MediaWiki_talk:Category|Talk]]
+&lt;/td&gt;&lt;td&gt;
+category
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Category</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Category_header&amp;action=edit category_header]&lt;br&gt;
+[[MediaWiki_talk:Category_header|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Articles in category &amp;quot;$1&amp;quot;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Category_header</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Changepassword&amp;action=edit changepassword]&lt;br&gt;
+[[MediaWiki_talk:Changepassword|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Change password
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Changepassword</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Changes&amp;action=edit changes]&lt;br&gt;
+[[MediaWiki_talk:Changes|Talk]]
+&lt;/td&gt;&lt;td&gt;
+changes
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Changes</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Columns&amp;action=edit columns]&lt;br&gt;
+[[MediaWiki_talk:Columns|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Columns
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Columns</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Commentedit&amp;action=edit commentedit]&lt;br&gt;
+[[MediaWiki_talk:Commentedit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+ (comment)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Commentedit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Compareselectedversions&amp;action=edit compareselectedversions]&lt;br&gt;
+[[MediaWiki_talk:Compareselectedversions|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Compare selected versions
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Compareselectedversions</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirm&amp;action=edit confirm]&lt;br&gt;
+[[MediaWiki_talk:Confirm|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Confirm
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Confirm</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmcheck&amp;action=edit confirmcheck]&lt;br&gt;
+[[MediaWiki_talk:Confirmcheck|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Yes, I really want to delete this.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Confirmcheck</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmdelete&amp;action=edit confirmdelete]&lt;br&gt;
+[[MediaWiki_talk:Confirmdelete|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Confirm delete
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Confirmdelete</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmdeletetext&amp;action=edit confirmdeletetext]&lt;br&gt;
+[[MediaWiki_talk:Confirmdeletetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You are about to permanently delete a page
+or image along with all of its history from the database.
+Please confirm that you intend to do this, that you understand the
+consequences, and that you are doing this in accordance with
+&amp;#91;&amp;#91;Wiktionary:Policy]].
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Confirmdeletetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmprotect&amp;action=edit confirmprotect]&lt;br&gt;
+[[MediaWiki_talk:Confirmprotect|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Confirm protection
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Confirmprotect</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmprotecttext&amp;action=edit confirmprotecttext]&lt;br&gt;
+[[MediaWiki_talk:Confirmprotecttext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Do you really want to protect this page?
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Confirmprotecttext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmunprotect&amp;action=edit confirmunprotect]&lt;br&gt;
+[[MediaWiki_talk:Confirmunprotect|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Confirm unprotection
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Confirmunprotect</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmunprotecttext&amp;action=edit confirmunprotecttext]&lt;br&gt;
+[[MediaWiki_talk:Confirmunprotecttext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Do you really want to unprotect this page?
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Confirmunprotecttext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Contextchars&amp;action=edit contextchars]&lt;br&gt;
+[[MediaWiki_talk:Contextchars|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Characters of context per line
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Contextchars</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Contextlines&amp;action=edit contextlines]&lt;br&gt;
+[[MediaWiki_talk:Contextlines|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Lines to show per hit
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Contextlines</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Contribslink&amp;action=edit contribslink]&lt;br&gt;
+[[MediaWiki_talk:Contribslink|Talk]]
+&lt;/td&gt;&lt;td&gt;
+contribs
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Contribslink</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Contribsub&amp;action=edit contribsub]&lt;br&gt;
+[[MediaWiki_talk:Contribsub|Talk]]
+&lt;/td&gt;&lt;td&gt;
+For $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Contribsub</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Contributions&amp;action=edit contributions]&lt;br&gt;
+[[MediaWiki_talk:Contributions|Talk]]
+&lt;/td&gt;&lt;td&gt;
+User contributions
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Contributions</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Copyright&amp;action=edit copyright]&lt;br&gt;
+[[MediaWiki_talk:Copyright|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Content is available under $1.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Copyright</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Copyrightpage&amp;action=edit copyrightpage]&lt;br&gt;
+[[MediaWiki_talk:Copyrightpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary:Copyrights
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Copyrightpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Copyrightpagename&amp;action=edit copyrightpagename]&lt;br&gt;
+[[MediaWiki_talk:Copyrightpagename|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary copyright
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Copyrightpagename</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Copyrightwarning&amp;action=edit copyrightwarning]&lt;br&gt;
+[[MediaWiki_talk:Copyrightwarning|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Please note that all contributions to Wiktionary are
+considered to be released under the GNU Free Documentation License
+(see $1 for details).
+If you don&amp;#39;t want your writing to be edited mercilessly and redistributed
+at will, then don&amp;#39;t submit it here.&amp;lt;br /&amp;gt;
+You are also promising us that you wrote this yourself, or copied it from a
+public domain or similar free resource.
+&amp;lt;strong&amp;gt;DO NOT SUBMIT COPYRIGHTED WORK WITHOUT PERMISSION!&amp;lt;/strong&amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Copyrightwarning</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Couldntremove&amp;action=edit couldntremove]&lt;br&gt;
+[[MediaWiki_talk:Couldntremove|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Couldn&amp;#39;t remove item &amp;#39;$1&amp;#39;...
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Couldntremove</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Createaccount&amp;action=edit createaccount]&lt;br&gt;
+[[MediaWiki_talk:Createaccount|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Create new account
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Createaccount</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Createaccountmail&amp;action=edit createaccountmail]&lt;br&gt;
+[[MediaWiki_talk:Createaccountmail|Talk]]
+&lt;/td&gt;&lt;td&gt;
+by email
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Createaccountmail</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Cur&amp;action=edit cur]&lt;br&gt;
+[[MediaWiki_talk:Cur|Talk]]
+&lt;/td&gt;&lt;td&gt;
+cur
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Cur</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Currentevents&amp;action=edit currentevents]&lt;br&gt;
+[[MediaWiki_talk:Currentevents|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Current events
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Currentevents</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Currentrev&amp;action=edit currentrev]&lt;br&gt;
+[[MediaWiki_talk:Currentrev|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Current revision
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Currentrev</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Databaseerror&amp;action=edit databaseerror]&lt;br&gt;
+[[MediaWiki_talk:Databaseerror|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Database error
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Databaseerror</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Dateformat&amp;action=edit dateformat]&lt;br&gt;
+[[MediaWiki_talk:Dateformat|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Date format
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Dateformat</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Dberrortext&amp;action=edit dberrortext]&lt;br&gt;
+[[MediaWiki_talk:Dberrortext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+A database query syntax error has occurred.
+This could be because of an illegal search query (see $5),
+or it may indicate a bug in the software.
+The last attempted database query was:
+&amp;lt;blockquote&amp;gt;&amp;lt;tt&amp;gt;$1&amp;lt;/tt&amp;gt;&amp;lt;/blockquote&amp;gt;
+from within function &amp;quot;&amp;lt;tt&amp;gt;$2&amp;lt;/tt&amp;gt;&amp;quot;.
+MySQL returned error &amp;quot;&amp;lt;tt&amp;gt;$3: $4&amp;lt;/tt&amp;gt;&amp;quot;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Dberrortext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Dberrortextcl&amp;action=edit dberrortextcl]&lt;br&gt;
+[[MediaWiki_talk:Dberrortextcl|Talk]]
+&lt;/td&gt;&lt;td&gt;
+A database query syntax error has occurred.
+The last attempted database query was:
+&amp;quot;$1&amp;quot;
+from within function &amp;quot;$2&amp;quot;.
+MySQL returned error &amp;quot;$3: $4&amp;quot;.
+
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Dberrortextcl</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deadendpages&amp;action=edit deadendpages]&lt;br&gt;
+[[MediaWiki_talk:Deadendpages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Dead-end pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Deadendpages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Debug&amp;action=edit debug]&lt;br&gt;
+[[MediaWiki_talk:Debug|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Debug
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Debug</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Defaultns&amp;action=edit defaultns]&lt;br&gt;
+[[MediaWiki_talk:Defaultns|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Search in these namespaces by default:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Defaultns</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Defemailsubject&amp;action=edit defemailsubject]&lt;br&gt;
+[[MediaWiki_talk:Defemailsubject|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary e-mail
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Defemailsubject</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Delete&amp;action=edit delete]&lt;br&gt;
+[[MediaWiki_talk:Delete|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Delete
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Delete</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletecomment&amp;action=edit deletecomment]&lt;br&gt;
+[[MediaWiki_talk:Deletecomment|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Reason for deletion
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Deletecomment</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletedarticle&amp;action=edit deletedarticle]&lt;br&gt;
+[[MediaWiki_talk:Deletedarticle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+deleted &amp;quot;$1&amp;quot;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Deletedarticle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletedtext&amp;action=edit deletedtext]&lt;br&gt;
+[[MediaWiki_talk:Deletedtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;quot;$1&amp;quot; has been deleted.
+See $2 for a record of recent deletions.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Deletedtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deleteimg&amp;action=edit deleteimg]&lt;br&gt;
+[[MediaWiki_talk:Deleteimg|Talk]]
+&lt;/td&gt;&lt;td&gt;
+del
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Deleteimg</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletepage&amp;action=edit deletepage]&lt;br&gt;
+[[MediaWiki_talk:Deletepage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Delete page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Deletepage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletesub&amp;action=edit deletesub]&lt;br&gt;
+[[MediaWiki_talk:Deletesub|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(Deleting &amp;quot;$1&amp;quot;)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Deletesub</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletethispage&amp;action=edit deletethispage]&lt;br&gt;
+[[MediaWiki_talk:Deletethispage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Delete this page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Deletethispage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletionlog&amp;action=edit deletionlog]&lt;br&gt;
+[[MediaWiki_talk:Deletionlog|Talk]]
+&lt;/td&gt;&lt;td&gt;
+deletion log
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Deletionlog</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Dellogpage&amp;action=edit dellogpage]&lt;br&gt;
+[[MediaWiki_talk:Dellogpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Deletion_log
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Dellogpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Dellogpagetext&amp;action=edit dellogpagetext]&lt;br&gt;
+[[MediaWiki_talk:Dellogpagetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Below is a list of the most recent deletions.
+All times shown are server time (UTC).
+&amp;lt;ul&amp;gt;
+&amp;lt;/ul&amp;gt;
+
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Dellogpagetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Developerspheading&amp;action=edit developerspheading]&lt;br&gt;
+[[MediaWiki_talk:Developerspheading|Talk]]
+&lt;/td&gt;&lt;td&gt;
+For developer use only
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Developerspheading</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Developertext&amp;action=edit developertext]&lt;br&gt;
+[[MediaWiki_talk:Developertext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The action you have requested can only be
+performed by users with &amp;quot;developer&amp;quot; status.
+See $1.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Developertext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Developertitle&amp;action=edit developertitle]&lt;br&gt;
+[[MediaWiki_talk:Developertitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Developer access required
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Developertitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Diff&amp;action=edit diff]&lt;br&gt;
+[[MediaWiki_talk:Diff|Talk]]
+&lt;/td&gt;&lt;td&gt;
+diff
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Diff</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Difference&amp;action=edit difference]&lt;br&gt;
+[[MediaWiki_talk:Difference|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(Difference between revisions)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Difference</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Disambiguations&amp;action=edit disambiguations]&lt;br&gt;
+[[MediaWiki_talk:Disambiguations|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Disambiguation pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Disambiguations</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Disambiguationspage&amp;action=edit disambiguationspage]&lt;br&gt;
+[[MediaWiki_talk:Disambiguationspage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary:Links_to_disambiguating_pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Disambiguationspage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Disambiguationstext&amp;action=edit disambiguationstext]&lt;br&gt;
+[[MediaWiki_talk:Disambiguationstext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The following pages link to a &amp;lt;i&amp;gt;disambiguation page&amp;lt;/i&amp;gt;. They should link to the appropriate topic instead.&amp;lt;br /&amp;gt;A page is treated as dismbiguation if it is linked from $1.&amp;lt;br /&amp;gt;Links from other namespaces are &amp;lt;i&amp;gt;not&amp;lt;/i&amp;gt; listed here.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Disambiguationstext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Disclaimerpage&amp;action=edit disclaimerpage]&lt;br&gt;
+[[MediaWiki_talk:Disclaimerpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary:General_disclaimer
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Disclaimerpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Disclaimers&amp;action=edit disclaimers]&lt;br&gt;
+[[MediaWiki_talk:Disclaimers|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Disclaimers
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Disclaimers</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Doubleredirects&amp;action=edit doubleredirects]&lt;br&gt;
+[[MediaWiki_talk:Doubleredirects|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Double Redirects
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Doubleredirects</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Doubleredirectstext&amp;action=edit doubleredirectstext]&lt;br&gt;
+[[MediaWiki_talk:Doubleredirectstext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;b&amp;gt;Attention:&amp;lt;/b&amp;gt; This list may contain false positives. That usually means there is additional text with links below the first #REDIRECT.&amp;lt;br /&amp;gt;
+Each row contains links to the first and second redirect, as well as the first line of the second redirect text, usually giving the &amp;quot;real&amp;quot; target page, which the first redirect should point to.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Doubleredirectstext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Edit&amp;action=edit edit]&lt;br&gt;
+[[MediaWiki_talk:Edit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Edit
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Edit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editcomment&amp;action=edit editcomment]&lt;br&gt;
+[[MediaWiki_talk:Editcomment|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The edit comment was: &amp;quot;&amp;lt;i&amp;gt;$1&amp;lt;/i&amp;gt;&amp;quot;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Editcomment</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editconflict&amp;action=edit editconflict]&lt;br&gt;
+[[MediaWiki_talk:Editconflict|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Edit conflict: $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Editconflict</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editcurrent&amp;action=edit editcurrent]&lt;br&gt;
+[[MediaWiki_talk:Editcurrent|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Edit the current version of this page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Editcurrent</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Edithelp&amp;action=edit edithelp]&lt;br&gt;
+[[MediaWiki_talk:Edithelp|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Editing help
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Edithelp</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Edithelppage&amp;action=edit edithelppage]&lt;br&gt;
+[[MediaWiki_talk:Edithelppage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Help:Editing
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Edithelppage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editing&amp;action=edit editing]&lt;br&gt;
+[[MediaWiki_talk:Editing|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Editing $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Editing</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editingold&amp;action=edit editingold]&lt;br&gt;
+[[MediaWiki_talk:Editingold|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;strong&amp;gt;WARNING: You are editing an out-of-date
+revision of this page.
+If you save it, any changes made since this revision will be lost.&amp;lt;/strong&amp;gt;
+
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Editingold</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editsection&amp;action=edit editsection]&lt;br&gt;
+[[MediaWiki_talk:Editsection|Talk]]
+&lt;/td&gt;&lt;td&gt;
+edit
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Editsection</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editthispage&amp;action=edit editthispage]&lt;br&gt;
+[[MediaWiki_talk:Editthispage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Edit this page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Editthispage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailflag&amp;action=edit emailflag]&lt;br&gt;
+[[MediaWiki_talk:Emailflag|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Disable e-mail from other users
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Emailflag</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailforlost&amp;action=edit emailforlost]&lt;br&gt;
+[[MediaWiki_talk:Emailforlost|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Fields marked with a star (*) are optional. Storing an email address enables people to contact you through the website without you having to reveal your
+email address to them, and it can be used to send you a new password if you forget it.&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;Your real name, if you choose to provide it, will be used for giving you attribution for your work.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Emailforlost</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailfrom&amp;action=edit emailfrom]&lt;br&gt;
+[[MediaWiki_talk:Emailfrom|Talk]]
+&lt;/td&gt;&lt;td&gt;
+From
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Emailfrom</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailmessage&amp;action=edit emailmessage]&lt;br&gt;
+[[MediaWiki_talk:Emailmessage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Message
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Emailmessage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailpage&amp;action=edit emailpage]&lt;br&gt;
+[[MediaWiki_talk:Emailpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+E-mail user
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Emailpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailpagetext&amp;action=edit emailpagetext]&lt;br&gt;
+[[MediaWiki_talk:Emailpagetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+If this user has entered a valid e-mail address in
+his or her user preferences, the form below will send a single message.
+The e-mail address you entered in your user preferences will appear
+as the &amp;quot;From&amp;quot; address of the mail, so the recipient will be able
+to reply.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Emailpagetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailsend&amp;action=edit emailsend]&lt;br&gt;
+[[MediaWiki_talk:Emailsend|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Send
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Emailsend</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailsent&amp;action=edit emailsent]&lt;br&gt;
+[[MediaWiki_talk:Emailsent|Talk]]
+&lt;/td&gt;&lt;td&gt;
+E-mail sent
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Emailsent</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailsenttext&amp;action=edit emailsenttext]&lt;br&gt;
+[[MediaWiki_talk:Emailsenttext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Your e-mail message has been sent.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Emailsenttext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailsubject&amp;action=edit emailsubject]&lt;br&gt;
+[[MediaWiki_talk:Emailsubject|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Subject
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Emailsubject</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailto&amp;action=edit emailto]&lt;br&gt;
+[[MediaWiki_talk:Emailto|Talk]]
+&lt;/td&gt;&lt;td&gt;
+To
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Emailto</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailuser&amp;action=edit emailuser]&lt;br&gt;
+[[MediaWiki_talk:Emailuser|Talk]]
+&lt;/td&gt;&lt;td&gt;
+E-mail this user
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Emailuser</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Enterlockreason&amp;action=edit enterlockreason]&lt;br&gt;
+[[MediaWiki_talk:Enterlockreason|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Enter a reason for the lock, including an estimate
+of when the lock will be released
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Enterlockreason</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Error&amp;action=edit error]&lt;br&gt;
+[[MediaWiki_talk:Error|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Error
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Error</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Errorpagetitle&amp;action=edit errorpagetitle]&lt;br&gt;
+[[MediaWiki_talk:Errorpagetitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Error
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Errorpagetitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Exbeforeblank&amp;action=edit exbeforeblank]&lt;br&gt;
+[[MediaWiki_talk:Exbeforeblank|Talk]]
+&lt;/td&gt;&lt;td&gt;
+content before blanking was:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Exbeforeblank</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Exblank&amp;action=edit exblank]&lt;br&gt;
+[[MediaWiki_talk:Exblank|Talk]]
+&lt;/td&gt;&lt;td&gt;
+page was empty
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Exblank</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Excontent&amp;action=edit excontent]&lt;br&gt;
+[[MediaWiki_talk:Excontent|Talk]]
+&lt;/td&gt;&lt;td&gt;
+content was:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Excontent</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Explainconflict&amp;action=edit explainconflict]&lt;br&gt;
+[[MediaWiki_talk:Explainconflict|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Someone else has changed this page since you
+started editing it.
+The upper text area contains the page text as it currently exists.
+Your changes are shown in the lower text area.
+You will have to merge your changes into the existing text.
+&amp;lt;b&amp;gt;Only&amp;lt;/b&amp;gt; the text in the upper text area will be saved when you
+press &amp;quot;Save page&amp;quot;.
+&amp;lt;p&amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Explainconflict</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Export&amp;action=edit export]&lt;br&gt;
+[[MediaWiki_talk:Export|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Export pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Export</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Exportcuronly&amp;action=edit exportcuronly]&lt;br&gt;
+[[MediaWiki_talk:Exportcuronly|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Include only the current revision, not the full history
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Exportcuronly</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Exporttext&amp;action=edit exporttext]&lt;br&gt;
+[[MediaWiki_talk:Exporttext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You can export the text and editing history of a particular
+page or set of pages wrapped in some XML; this can then be imported into another
+wiki running MediaWiki software, transformed, or just kept for your private
+amusement.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Exporttext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Extlink_sample&amp;action=edit extlink_sample]&lt;br&gt;
+[[MediaWiki_talk:Extlink_sample|Talk]]
+&lt;/td&gt;&lt;td&gt;
+http&amp;#58;//www.example.com link title
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Extlink_sample</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Extlink_tip&amp;action=edit extlink_tip]&lt;br&gt;
+[[MediaWiki_talk:Extlink_tip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+External link (remember http&amp;#58;// prefix)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Extlink_tip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Faq&amp;action=edit faq]&lt;br&gt;
+[[MediaWiki_talk:Faq|Talk]]
+&lt;/td&gt;&lt;td&gt;
+FAQ
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Faq</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Faqpage&amp;action=edit faqpage]&lt;br&gt;
+[[MediaWiki_talk:Faqpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary:FAQ
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Faqpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Feedlinks&amp;action=edit feedlinks]&lt;br&gt;
+[[MediaWiki_talk:Feedlinks|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Feed:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Feedlinks</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filecopyerror&amp;action=edit filecopyerror]&lt;br&gt;
+[[MediaWiki_talk:Filecopyerror|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Could not copy file &amp;quot;$1&amp;quot; to &amp;quot;$2&amp;quot;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Filecopyerror</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filedeleteerror&amp;action=edit filedeleteerror]&lt;br&gt;
+[[MediaWiki_talk:Filedeleteerror|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Could not delete file &amp;quot;$1&amp;quot;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Filedeleteerror</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filedesc&amp;action=edit filedesc]&lt;br&gt;
+[[MediaWiki_talk:Filedesc|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Summary
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Filedesc</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filename&amp;action=edit filename]&lt;br&gt;
+[[MediaWiki_talk:Filename|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Filename
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Filename</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filenotfound&amp;action=edit filenotfound]&lt;br&gt;
+[[MediaWiki_talk:Filenotfound|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Could not find file &amp;quot;$1&amp;quot;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Filenotfound</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filerenameerror&amp;action=edit filerenameerror]&lt;br&gt;
+[[MediaWiki_talk:Filerenameerror|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Could not rename file &amp;quot;$1&amp;quot; to &amp;quot;$2&amp;quot;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Filerenameerror</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filesource&amp;action=edit filesource]&lt;br&gt;
+[[MediaWiki_talk:Filesource|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Source
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Filesource</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filestatus&amp;action=edit filestatus]&lt;br&gt;
+[[MediaWiki_talk:Filestatus|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Copyright status
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Filestatus</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Fileuploaded&amp;action=edit fileuploaded]&lt;br&gt;
+[[MediaWiki_talk:Fileuploaded|Talk]]
+&lt;/td&gt;&lt;td&gt;
+File &amp;quot;$1&amp;quot; uploaded successfully.
+Please follow this link: $2 to the description page and fill
+in information about the file, such as where it came from, when it was
+created and by whom, and anything else you may know about it.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Fileuploaded</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Formerror&amp;action=edit formerror]&lt;br&gt;
+[[MediaWiki_talk:Formerror|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Error: could not submit form
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Formerror</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Fromwikipedia&amp;action=edit fromwikipedia]&lt;br&gt;
+[[MediaWiki_talk:Fromwikipedia|Talk]]
+&lt;/td&gt;&lt;td&gt;
+From Wiktionary
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Fromwikipedia</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Getimagelist&amp;action=edit getimagelist]&lt;br&gt;
+[[MediaWiki_talk:Getimagelist|Talk]]
+&lt;/td&gt;&lt;td&gt;
+fetching image list
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Getimagelist</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Go&amp;action=edit go]&lt;br&gt;
+[[MediaWiki_talk:Go|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Go
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Go</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Googlesearch&amp;action=edit googlesearch]&lt;br&gt;
+[[MediaWiki_talk:Googlesearch|Talk]]
+&lt;/td&gt;&lt;td&gt;
+
+&amp;lt;!-- SiteSearch Google --&amp;gt;
+&amp;lt;FORM method=GET action=&amp;quot;http&amp;#58;//www.google.com/search&amp;quot;&amp;gt;
+&amp;lt;TABLE bgcolor=&amp;quot;#FFFFFF&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;
+&amp;lt;A HREF=&amp;quot;http&amp;#58;//www.google.com/&amp;quot;&amp;gt;
+&amp;lt;IMG SRC=&amp;quot;http&amp;#58;//www.google.com/logos/Logo_40wht.gif&amp;quot;
+border=&amp;quot;0&amp;quot; ALT=&amp;quot;Google&amp;quot;&amp;gt;&amp;lt;/A&amp;gt;
+&amp;lt;/td&amp;gt;
+&amp;lt;td&amp;gt;
+&amp;lt;INPUT TYPE=text name=q size=31 maxlength=255 value=&amp;quot;$1&amp;quot;&amp;gt;
+&amp;lt;INPUT type=submit name=btnG VALUE=&amp;quot;Google Search&amp;quot;&amp;gt;
+&amp;lt;font size=-1&amp;gt;
+&amp;lt;input type=hidden name=domains value=&amp;quot;http&amp;#58;//tl.wiktionary.org&amp;quot;&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;input type=radio name=sitesearch value=&amp;quot;&amp;quot;&amp;gt; WWW &amp;lt;input type=radio name=sitesearch value=&amp;quot;http&amp;#58;//tl.wiktionary.org&amp;quot; checked&amp;gt; http&amp;#58;//tl.wiktionary.org &amp;lt;br /&amp;gt;
+&amp;lt;input type=&amp;#39;hidden&amp;#39; name=&amp;#39;ie&amp;#39; value=&amp;#39;$2&amp;#39;&amp;gt;
+&amp;lt;input type=&amp;#39;hidden&amp;#39; name=&amp;#39;oe&amp;#39; value=&amp;#39;$2&amp;#39;&amp;gt;
+&amp;lt;/font&amp;gt;
+&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/TABLE&amp;gt;
+&amp;lt;/FORM&amp;gt;
+&amp;lt;!-- SiteSearch Google --&amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Googlesearch</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Guesstimezone&amp;action=edit guesstimezone]&lt;br&gt;
+[[MediaWiki_talk:Guesstimezone|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Fill in from browser
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Guesstimezone</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Headline_sample&amp;action=edit headline_sample]&lt;br&gt;
+[[MediaWiki_talk:Headline_sample|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Headline text
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Headline_sample</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Headline_tip&amp;action=edit headline_tip]&lt;br&gt;
+[[MediaWiki_talk:Headline_tip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Level 2 headline
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Headline_tip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Help&amp;action=edit help]&lt;br&gt;
+[[MediaWiki_talk:Help|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Help
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Help</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Helppage&amp;action=edit helppage]&lt;br&gt;
+[[MediaWiki_talk:Helppage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Help:Contents
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Helppage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Hide&amp;action=edit hide]&lt;br&gt;
+[[MediaWiki_talk:Hide|Talk]]
+&lt;/td&gt;&lt;td&gt;
+hide
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Hide</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Hidetoc&amp;action=edit hidetoc]&lt;br&gt;
+[[MediaWiki_talk:Hidetoc|Talk]]
+&lt;/td&gt;&lt;td&gt;
+hide
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Hidetoc</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Hist&amp;action=edit hist]&lt;br&gt;
+[[MediaWiki_talk:Hist|Talk]]
+&lt;/td&gt;&lt;td&gt;
+hist
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Hist</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Histlegend&amp;action=edit histlegend]&lt;br&gt;
+[[MediaWiki_talk:Histlegend|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Diff selection: mark the radio boxes of the versions to compare and hit enter or the button at the bottom.&amp;lt;br/&amp;gt;
+Legend: (cur) = difference with current version,
+(last) = difference with preceding version, M = minor edit.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Histlegend</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:History&amp;action=edit history]&lt;br&gt;
+[[MediaWiki_talk:History|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Page history
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:History</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:History_short&amp;action=edit history_short]&lt;br&gt;
+[[MediaWiki_talk:History_short|Talk]]
+&lt;/td&gt;&lt;td&gt;
+History
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:History_short</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Historywarning&amp;action=edit historywarning]&lt;br&gt;
+[[MediaWiki_talk:Historywarning|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Warning: The page you are about to delete has a history:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Historywarning</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Hr_tip&amp;action=edit hr_tip]&lt;br&gt;
+[[MediaWiki_talk:Hr_tip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Horizontal line (use sparingly)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Hr_tip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ignorewarning&amp;action=edit ignorewarning]&lt;br&gt;
+[[MediaWiki_talk:Ignorewarning|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Ignore warning and save file anyway.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ignorewarning</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ilshowmatch&amp;action=edit ilshowmatch]&lt;br&gt;
+[[MediaWiki_talk:Ilshowmatch|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Show all images with names matching
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ilshowmatch</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ilsubmit&amp;action=edit ilsubmit]&lt;br&gt;
+[[MediaWiki_talk:Ilsubmit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Search
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ilsubmit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Image_sample&amp;action=edit image_sample]&lt;br&gt;
+[[MediaWiki_talk:Image_sample|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Example.jpg
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Image_sample</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Image_tip&amp;action=edit image_tip]&lt;br&gt;
+[[MediaWiki_talk:Image_tip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Embedded image
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Image_tip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imagelinks&amp;action=edit imagelinks]&lt;br&gt;
+[[MediaWiki_talk:Imagelinks|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Image links
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Imagelinks</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imagelist&amp;action=edit imagelist]&lt;br&gt;
+[[MediaWiki_talk:Imagelist|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Image list
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Imagelist</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imagelisttext&amp;action=edit imagelisttext]&lt;br&gt;
+[[MediaWiki_talk:Imagelisttext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Below is a list of $1 images sorted $2.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Imagelisttext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imagepage&amp;action=edit imagepage]&lt;br&gt;
+[[MediaWiki_talk:Imagepage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View image page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Imagepage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imagereverted&amp;action=edit imagereverted]&lt;br&gt;
+[[MediaWiki_talk:Imagereverted|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Revert to earlier version was successful.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Imagereverted</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imgdelete&amp;action=edit imgdelete]&lt;br&gt;
+[[MediaWiki_talk:Imgdelete|Talk]]
+&lt;/td&gt;&lt;td&gt;
+del
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Imgdelete</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imgdesc&amp;action=edit imgdesc]&lt;br&gt;
+[[MediaWiki_talk:Imgdesc|Talk]]
+&lt;/td&gt;&lt;td&gt;
+desc
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Imgdesc</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imghistlegend&amp;action=edit imghistlegend]&lt;br&gt;
+[[MediaWiki_talk:Imghistlegend|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Legend: (cur) = this is the current image, (del) = delete
+this old version, (rev) = revert to this old version.
+&amp;lt;br /&amp;gt;&amp;lt;i&amp;gt;Click on date to see image uploaded on that date&amp;lt;/i&amp;gt;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Imghistlegend</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imghistory&amp;action=edit imghistory]&lt;br&gt;
+[[MediaWiki_talk:Imghistory|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Image history
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Imghistory</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imglegend&amp;action=edit imglegend]&lt;br&gt;
+[[MediaWiki_talk:Imglegend|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Legend: (desc) = show/edit image description.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Imglegend</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Import&amp;action=edit import]&lt;br&gt;
+[[MediaWiki_talk:Import|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Import pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Import</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Importfailed&amp;action=edit importfailed]&lt;br&gt;
+[[MediaWiki_talk:Importfailed|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Import failed: $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Importfailed</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Importhistoryconflict&amp;action=edit importhistoryconflict]&lt;br&gt;
+[[MediaWiki_talk:Importhistoryconflict|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Conflicting history revision exists (may have imported this page before)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Importhistoryconflict</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Importnotext&amp;action=edit importnotext]&lt;br&gt;
+[[MediaWiki_talk:Importnotext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Empty or no text
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Importnotext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Importsuccess&amp;action=edit importsuccess]&lt;br&gt;
+[[MediaWiki_talk:Importsuccess|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Import succeeded!
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Importsuccess</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Importtext&amp;action=edit importtext]&lt;br&gt;
+[[MediaWiki_talk:Importtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Please export the file from the source wiki using the Special:Export utility, save it to your disk and upload it here.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Importtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Infobox&amp;action=edit infobox]&lt;br&gt;
+[[MediaWiki_talk:Infobox|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Click a button to get an example text
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Infobox</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Infobox_alert&amp;action=edit infobox_alert]&lt;br&gt;
+[[MediaWiki_talk:Infobox_alert|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Please enter the text you want to be formatted.\n It will be shown in the infobox for copy and pasting.\nExample:\n$1\nwill become:\n$2
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Infobox_alert</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Internalerror&amp;action=edit internalerror]&lt;br&gt;
+[[MediaWiki_talk:Internalerror|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Internal error
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Internalerror</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Intl&amp;action=edit intl]&lt;br&gt;
+[[MediaWiki_talk:Intl|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Interlanguage links
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Intl</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ip_range_invalid&amp;action=edit ip_range_invalid]&lt;br&gt;
+[[MediaWiki_talk:Ip_range_invalid|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Invalid IP range.
+
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ip_range_invalid</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipaddress&amp;action=edit ipaddress]&lt;br&gt;
+[[MediaWiki_talk:Ipaddress|Talk]]
+&lt;/td&gt;&lt;td&gt;
+IP Address/username
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ipaddress</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipb_expiry_invalid&amp;action=edit ipb_expiry_invalid]&lt;br&gt;
+[[MediaWiki_talk:Ipb_expiry_invalid|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Expiry time invalid.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ipb_expiry_invalid</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipbexpiry&amp;action=edit ipbexpiry]&lt;br&gt;
+[[MediaWiki_talk:Ipbexpiry|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Expiry
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ipbexpiry</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipblocklist&amp;action=edit ipblocklist]&lt;br&gt;
+[[MediaWiki_talk:Ipblocklist|Talk]]
+&lt;/td&gt;&lt;td&gt;
+List of blocked IP addresses and usernames
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ipblocklist</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipbreason&amp;action=edit ipbreason]&lt;br&gt;
+[[MediaWiki_talk:Ipbreason|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Reason
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ipbreason</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipbsubmit&amp;action=edit ipbsubmit]&lt;br&gt;
+[[MediaWiki_talk:Ipbsubmit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Block this user
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ipbsubmit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipusubmit&amp;action=edit ipusubmit]&lt;br&gt;
+[[MediaWiki_talk:Ipusubmit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Unblock this address
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ipusubmit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipusuccess&amp;action=edit ipusuccess]&lt;br&gt;
+[[MediaWiki_talk:Ipusuccess|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;quot;$1&amp;quot; unblocked
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ipusuccess</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Isbn&amp;action=edit isbn]&lt;br&gt;
+[[MediaWiki_talk:Isbn|Talk]]
+&lt;/td&gt;&lt;td&gt;
+ISBN
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Isbn</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Isredirect&amp;action=edit isredirect]&lt;br&gt;
+[[MediaWiki_talk:Isredirect|Talk]]
+&lt;/td&gt;&lt;td&gt;
+redirect page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Isredirect</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Italic_sample&amp;action=edit italic_sample]&lt;br&gt;
+[[MediaWiki_talk:Italic_sample|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Italic text
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Italic_sample</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Italic_tip&amp;action=edit italic_tip]&lt;br&gt;
+[[MediaWiki_talk:Italic_tip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Italic text
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Italic_tip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Iteminvalidname&amp;action=edit iteminvalidname]&lt;br&gt;
+[[MediaWiki_talk:Iteminvalidname|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Problem with item &amp;#39;$1&amp;#39;, invalid name...
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Iteminvalidname</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Largefile&amp;action=edit largefile]&lt;br&gt;
+[[MediaWiki_talk:Largefile|Talk]]
+&lt;/td&gt;&lt;td&gt;
+It is recommended that images not exceed 100k in size.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Largefile</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Last&amp;action=edit last]&lt;br&gt;
+[[MediaWiki_talk:Last|Talk]]
+&lt;/td&gt;&lt;td&gt;
+last
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Last</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lastmodified&amp;action=edit lastmodified]&lt;br&gt;
+[[MediaWiki_talk:Lastmodified|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This page was last modified $1.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Lastmodified</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lastmodifiedby&amp;action=edit lastmodifiedby]&lt;br&gt;
+[[MediaWiki_talk:Lastmodifiedby|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This page was last modified $1 by $2.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Lastmodifiedby</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lineno&amp;action=edit lineno]&lt;br&gt;
+[[MediaWiki_talk:Lineno|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Line $1:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Lineno</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Link_sample&amp;action=edit link_sample]&lt;br&gt;
+[[MediaWiki_talk:Link_sample|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Link title
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Link_sample</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Link_tip&amp;action=edit link_tip]&lt;br&gt;
+[[MediaWiki_talk:Link_tip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Internal link
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Link_tip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Linklistsub&amp;action=edit linklistsub]&lt;br&gt;
+[[MediaWiki_talk:Linklistsub|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(List of links)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Linklistsub</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Linkshere&amp;action=edit linkshere]&lt;br&gt;
+[[MediaWiki_talk:Linkshere|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The following pages link to here:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Linkshere</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Linkstoimage&amp;action=edit linkstoimage]&lt;br&gt;
+[[MediaWiki_talk:Linkstoimage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The following pages link to this image:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Linkstoimage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Linktrail&amp;action=edit linktrail]&lt;br&gt;
+[[MediaWiki_talk:Linktrail|Talk]]
+&lt;/td&gt;&lt;td&gt;
+/^(&amp;#91;a-z]+)(.*)$/sD
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Linktrail</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Listform&amp;action=edit listform]&lt;br&gt;
+[[MediaWiki_talk:Listform|Talk]]
+&lt;/td&gt;&lt;td&gt;
+list
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Listform</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Listusers&amp;action=edit listusers]&lt;br&gt;
+[[MediaWiki_talk:Listusers|Talk]]
+&lt;/td&gt;&lt;td&gt;
+User list
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Listusers</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loadhist&amp;action=edit loadhist]&lt;br&gt;
+[[MediaWiki_talk:Loadhist|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Loading page history
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Loadhist</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loadingrev&amp;action=edit loadingrev]&lt;br&gt;
+[[MediaWiki_talk:Loadingrev|Talk]]
+&lt;/td&gt;&lt;td&gt;
+loading revision for diff
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Loadingrev</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Localtime&amp;action=edit localtime]&lt;br&gt;
+[[MediaWiki_talk:Localtime|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Local time display
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Localtime</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lockbtn&amp;action=edit lockbtn]&lt;br&gt;
+[[MediaWiki_talk:Lockbtn|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Lock database
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Lockbtn</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lockconfirm&amp;action=edit lockconfirm]&lt;br&gt;
+[[MediaWiki_talk:Lockconfirm|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Yes, I really want to lock the database.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Lockconfirm</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lockdb&amp;action=edit lockdb]&lt;br&gt;
+[[MediaWiki_talk:Lockdb|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Lock database
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Lockdb</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lockdbsuccesssub&amp;action=edit lockdbsuccesssub]&lt;br&gt;
+[[MediaWiki_talk:Lockdbsuccesssub|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Database lock succeeded
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Lockdbsuccesssub</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lockdbsuccesstext&amp;action=edit lockdbsuccesstext]&lt;br&gt;
+[[MediaWiki_talk:Lockdbsuccesstext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The database has been locked.
+&amp;lt;br /&amp;gt;Remember to remove the lock after your maintenance is complete.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Lockdbsuccesstext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lockdbtext&amp;action=edit lockdbtext]&lt;br&gt;
+[[MediaWiki_talk:Lockdbtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Locking the database will suspend the ability of all
+users to edit pages, change their preferences, edit their watchlists, and
+other things requiring changes in the database.
+Please confirm that this is what you intend to do, and that you will
+unlock the database when your maintenance is done.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Lockdbtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Locknoconfirm&amp;action=edit locknoconfirm]&lt;br&gt;
+[[MediaWiki_talk:Locknoconfirm|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You did not check the confirmation box.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Locknoconfirm</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Login&amp;action=edit login]&lt;br&gt;
+[[MediaWiki_talk:Login|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Log in
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Login</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginend&amp;action=edit loginend]&lt;br&gt;
+[[MediaWiki_talk:Loginend|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;amp;nbsp;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Loginend</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginerror&amp;action=edit loginerror]&lt;br&gt;
+[[MediaWiki_talk:Loginerror|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Login error
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Loginerror</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginpagetitle&amp;action=edit loginpagetitle]&lt;br&gt;
+[[MediaWiki_talk:Loginpagetitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+User login
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Loginpagetitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginproblem&amp;action=edit loginproblem]&lt;br&gt;
+[[MediaWiki_talk:Loginproblem|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;b&amp;gt;There has been a problem with your login.&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;Try again!
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Loginproblem</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginprompt&amp;action=edit loginprompt]&lt;br&gt;
+[[MediaWiki_talk:Loginprompt|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You must have cookies enabled to log in to Wiktionary.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Loginprompt</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginreqtext&amp;action=edit loginreqtext]&lt;br&gt;
+[[MediaWiki_talk:Loginreqtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You must &amp;#91;&amp;#91;special:Userlogin&amp;#124;login]] to view other pages.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Loginreqtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginreqtitle&amp;action=edit loginreqtitle]&lt;br&gt;
+[[MediaWiki_talk:Loginreqtitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Login Required
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Loginreqtitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginsuccess&amp;action=edit loginsuccess]&lt;br&gt;
+[[MediaWiki_talk:Loginsuccess|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You are now logged in to Wiktionary as &amp;quot;$1&amp;quot;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Loginsuccess</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginsuccesstitle&amp;action=edit loginsuccesstitle]&lt;br&gt;
+[[MediaWiki_talk:Loginsuccesstitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Login successful
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Loginsuccesstitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Logout&amp;action=edit logout]&lt;br&gt;
+[[MediaWiki_talk:Logout|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Log out
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Logout</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Logouttext&amp;action=edit logouttext]&lt;br&gt;
+[[MediaWiki_talk:Logouttext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You are now logged out.
+You can continue to use Wiktionary anonymously, or you can log in
+again as the same or as a different user. Note that some pages may
+continue to be displayed as if you were still logged in, until you clear
+your browser cache
+
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Logouttext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Logouttitle&amp;action=edit logouttitle]&lt;br&gt;
+[[MediaWiki_talk:Logouttitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+User logout
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Logouttitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lonelypages&amp;action=edit lonelypages]&lt;br&gt;
+[[MediaWiki_talk:Lonelypages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Orphaned pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Lonelypages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Longpages&amp;action=edit longpages]&lt;br&gt;
+[[MediaWiki_talk:Longpages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Long pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Longpages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Longpagewarning&amp;action=edit longpagewarning]&lt;br&gt;
+[[MediaWiki_talk:Longpagewarning|Talk]]
+&lt;/td&gt;&lt;td&gt;
+WARNING: This page is $1 kilobytes long; some
+browsers may have problems editing pages approaching or longer than 32kb.
+Please consider breaking the page into smaller sections.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Longpagewarning</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mailerror&amp;action=edit mailerror]&lt;br&gt;
+[[MediaWiki_talk:Mailerror|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Error sending mail: $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mailerror</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mailmypassword&amp;action=edit mailmypassword]&lt;br&gt;
+[[MediaWiki_talk:Mailmypassword|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Mail me a new password
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mailmypassword</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mailnologin&amp;action=edit mailnologin]&lt;br&gt;
+[[MediaWiki_talk:Mailnologin|Talk]]
+&lt;/td&gt;&lt;td&gt;
+No send address
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mailnologin</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mailnologintext&amp;action=edit mailnologintext]&lt;br&gt;
+[[MediaWiki_talk:Mailnologintext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You must be &amp;lt;a href=&amp;quot;{{localurl:Special:Userlogin&amp;quot;&amp;gt;logged in&amp;lt;/a&amp;gt;
+and have a valid e-mail address in your &amp;lt;a href=&amp;quot;/wiki/Special:Preferences&amp;quot;&amp;gt;preferences&amp;lt;/a&amp;gt;
+to send e-mail to other users.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mailnologintext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mainpage&amp;action=edit mainpage]&lt;br&gt;
+[[MediaWiki_talk:Mainpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Main Page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mainpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mainpagedocfooter&amp;action=edit mainpagedocfooter]&lt;br&gt;
+[[MediaWiki_talk:Mainpagedocfooter|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Please see &amp;#91;http&amp;#58;//meta.wikipedia.org/wiki/MediaWiki_i18n documentation on customizing the interface]
+and the &amp;#91;http&amp;#58;//meta.wikipedia.org/wiki/MediaWiki_User%27s_Guide User&amp;#39;s Guide] for usage and configuration help.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mainpagedocfooter</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mainpagetext&amp;action=edit mainpagetext]&lt;br&gt;
+[[MediaWiki_talk:Mainpagetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiki software successfully installed.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mainpagetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Maintenance&amp;action=edit maintenance]&lt;br&gt;
+[[MediaWiki_talk:Maintenance|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Maintenance page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Maintenance</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Maintenancebacklink&amp;action=edit maintenancebacklink]&lt;br&gt;
+[[MediaWiki_talk:Maintenancebacklink|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Back to Maintenance Page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Maintenancebacklink</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Maintnancepagetext&amp;action=edit maintnancepagetext]&lt;br&gt;
+[[MediaWiki_talk:Maintnancepagetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This page includes several handy tools for everyday maintenance. Some of these functions tend to stress the database, so please do not hit reload after every item you fixed ;-)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Maintnancepagetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysop&amp;action=edit makesysop]&lt;br&gt;
+[[MediaWiki_talk:Makesysop|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Make a user into a sysop
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Makesysop</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysopfail&amp;action=edit makesysopfail]&lt;br&gt;
+[[MediaWiki_talk:Makesysopfail|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;b&amp;gt;User &amp;quot;$1&amp;quot; could not be made into a sysop. (Did you enter the name correctly?)&amp;lt;/b&amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Makesysopfail</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysopname&amp;action=edit makesysopname]&lt;br&gt;
+[[MediaWiki_talk:Makesysopname|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Name of the user:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Makesysopname</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysopok&amp;action=edit makesysopok]&lt;br&gt;
+[[MediaWiki_talk:Makesysopok|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;b&amp;gt;User &amp;quot;$1&amp;quot; is now a sysop&amp;lt;/b&amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Makesysopok</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysopsubmit&amp;action=edit makesysopsubmit]&lt;br&gt;
+[[MediaWiki_talk:Makesysopsubmit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Make this user into a sysop
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Makesysopsubmit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysoptext&amp;action=edit makesysoptext]&lt;br&gt;
+[[MediaWiki_talk:Makesysoptext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This form is used by bureaucrats to turn ordinary users into administrators.
+Type the name of the user in the box and press the button to make the user an administrator
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Makesysoptext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysoptitle&amp;action=edit makesysoptitle]&lt;br&gt;
+[[MediaWiki_talk:Makesysoptitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Make a user into a sysop
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Makesysoptitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Matchtotals&amp;action=edit matchtotals]&lt;br&gt;
+[[MediaWiki_talk:Matchtotals|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The query &amp;quot;$1&amp;quot; matched $2 page titles
+and the text of $3 pages.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Matchtotals</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math&amp;action=edit math]&lt;br&gt;
+[[MediaWiki_talk:Math|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Rendering math
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Math</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_bad_output&amp;action=edit math_bad_output]&lt;br&gt;
+[[MediaWiki_talk:Math_bad_output|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Can&amp;#39;t write to or create math output directory
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Math_bad_output</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_bad_tmpdir&amp;action=edit math_bad_tmpdir]&lt;br&gt;
+[[MediaWiki_talk:Math_bad_tmpdir|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Can&amp;#39;t write to or create math temp directory
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Math_bad_tmpdir</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_failure&amp;action=edit math_failure]&lt;br&gt;
+[[MediaWiki_talk:Math_failure|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Failed to parse
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Math_failure</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_image_error&amp;action=edit math_image_error]&lt;br&gt;
+[[MediaWiki_talk:Math_image_error|Talk]]
+&lt;/td&gt;&lt;td&gt;
+PNG conversion failed; check for correct installation of latex, dvips, gs, and convert
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Math_image_error</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_lexing_error&amp;action=edit math_lexing_error]&lt;br&gt;
+[[MediaWiki_talk:Math_lexing_error|Talk]]
+&lt;/td&gt;&lt;td&gt;
+lexing error
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Math_lexing_error</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_notexvc&amp;action=edit math_notexvc]&lt;br&gt;
+[[MediaWiki_talk:Math_notexvc|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Missing texvc executable; please see math/README to configure.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Math_notexvc</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_sample&amp;action=edit math_sample]&lt;br&gt;
+[[MediaWiki_talk:Math_sample|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Insert formula here
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Math_sample</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_syntax_error&amp;action=edit math_syntax_error]&lt;br&gt;
+[[MediaWiki_talk:Math_syntax_error|Talk]]
+&lt;/td&gt;&lt;td&gt;
+syntax error
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Math_syntax_error</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_tip&amp;action=edit math_tip]&lt;br&gt;
+[[MediaWiki_talk:Math_tip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Mathematical formula (LaTeX)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Math_tip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_unknown_error&amp;action=edit math_unknown_error]&lt;br&gt;
+[[MediaWiki_talk:Math_unknown_error|Talk]]
+&lt;/td&gt;&lt;td&gt;
+unknown error
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Math_unknown_error</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_unknown_function&amp;action=edit math_unknown_function]&lt;br&gt;
+[[MediaWiki_talk:Math_unknown_function|Talk]]
+&lt;/td&gt;&lt;td&gt;
+unknown function
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Math_unknown_function</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Media_sample&amp;action=edit media_sample]&lt;br&gt;
+[[MediaWiki_talk:Media_sample|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Example.mp3
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Media_sample</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Media_tip&amp;action=edit media_tip]&lt;br&gt;
+[[MediaWiki_talk:Media_tip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Media file link
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Media_tip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Minlength&amp;action=edit minlength]&lt;br&gt;
+[[MediaWiki_talk:Minlength|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Image names must be at least three letters.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Minlength</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Minoredit&amp;action=edit minoredit]&lt;br&gt;
+[[MediaWiki_talk:Minoredit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This is a minor edit
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Minoredit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Minoreditletter&amp;action=edit minoreditletter]&lt;br&gt;
+[[MediaWiki_talk:Minoreditletter|Talk]]
+&lt;/td&gt;&lt;td&gt;
+M
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Minoreditletter</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mispeelings&amp;action=edit mispeelings]&lt;br&gt;
+[[MediaWiki_talk:Mispeelings|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Pages with misspellings
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mispeelings</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mispeelingspage&amp;action=edit mispeelingspage]&lt;br&gt;
+[[MediaWiki_talk:Mispeelingspage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+List of common misspellings
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mispeelingspage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mispeelingstext&amp;action=edit mispeelingstext]&lt;br&gt;
+[[MediaWiki_talk:Mispeelingstext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The following pages contain a common misspelling, which are listed on $1. The correct spelling might be given (like this).
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mispeelingstext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Missingarticle&amp;action=edit missingarticle]&lt;br&gt;
+[[MediaWiki_talk:Missingarticle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The database did not find the text of a page
+that it should have found, named &amp;quot;$1&amp;quot;.
+
+&amp;lt;p&amp;gt;This is usually caused by following an outdated diff or history link to a
+page that has been deleted.
+
+&amp;lt;p&amp;gt;If this is not the case, you may have found a bug in the software.
+Please report this to an administrator, making note of the URL.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Missingarticle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Missingimage&amp;action=edit missingimage]&lt;br&gt;
+[[MediaWiki_talk:Missingimage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;b&amp;gt;Missing image&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;i&amp;gt;$1&amp;lt;/i&amp;gt;
+
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Missingimage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Missinglanguagelinks&amp;action=edit missinglanguagelinks]&lt;br&gt;
+[[MediaWiki_talk:Missinglanguagelinks|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Missing Language Links
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Missinglanguagelinks</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Missinglanguagelinksbutton&amp;action=edit missinglanguagelinksbutton]&lt;br&gt;
+[[MediaWiki_talk:Missinglanguagelinksbutton|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Find missing language links for
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Missinglanguagelinksbutton</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Missinglanguagelinkstext&amp;action=edit missinglanguagelinkstext]&lt;br&gt;
+[[MediaWiki_talk:Missinglanguagelinkstext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+These pages do &amp;lt;i&amp;gt;not&amp;lt;/i&amp;gt; link to their counterpart in $1. Redirects and subpages are &amp;lt;i&amp;gt;not&amp;lt;/i&amp;gt; shown.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Missinglanguagelinkstext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Moredotdotdot&amp;action=edit moredotdotdot]&lt;br&gt;
+[[MediaWiki_talk:Moredotdotdot|Talk]]
+&lt;/td&gt;&lt;td&gt;
+More...
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Moredotdotdot</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Move&amp;action=edit move]&lt;br&gt;
+[[MediaWiki_talk:Move|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Move
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Move</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movearticle&amp;action=edit movearticle]&lt;br&gt;
+[[MediaWiki_talk:Movearticle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Move page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Movearticle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movedto&amp;action=edit movedto]&lt;br&gt;
+[[MediaWiki_talk:Movedto|Talk]]
+&lt;/td&gt;&lt;td&gt;
+moved to
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Movedto</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movenologin&amp;action=edit movenologin]&lt;br&gt;
+[[MediaWiki_talk:Movenologin|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Not logged in
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Movenologin</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movenologintext&amp;action=edit movenologintext]&lt;br&gt;
+[[MediaWiki_talk:Movenologintext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You must be a registered user and &amp;lt;a href=&amp;quot;/wiki/Special:Userlogin&amp;quot;&amp;gt;logged in&amp;lt;/a&amp;gt;
+to move a page.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Movenologintext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movepage&amp;action=edit movepage]&lt;br&gt;
+[[MediaWiki_talk:Movepage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Move page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Movepage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movepagebtn&amp;action=edit movepagebtn]&lt;br&gt;
+[[MediaWiki_talk:Movepagebtn|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Move page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Movepagebtn</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movepagetalktext&amp;action=edit movepagetalktext]&lt;br&gt;
+[[MediaWiki_talk:Movepagetalktext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The associated talk page, if any, will be automatically moved along with it &amp;#39;&amp;#39;&amp;#39;unless:&amp;#39;&amp;#39;&amp;#39;
+*You are moving the page across namespaces,
+*A non-empty talk page already exists under the new name, or
+*You uncheck the box below.
+
+In those cases, you will have to move or merge the page manually if desired.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Movepagetalktext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movepagetext&amp;action=edit movepagetext]&lt;br&gt;
+[[MediaWiki_talk:Movepagetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Using the form below will rename a page, moving all
+of its history to the new name.
+The old title will become a redirect page to the new title.
+Links to the old page title will not be changed; be sure to
+&amp;#91;&amp;#91;Special:Maintenance&amp;#124;check]] for double or broken redirects.
+You are responsible for making sure that links continue to
+point where they are supposed to go.
+
+Note that the page will &amp;#39;&amp;#39;&amp;#39;not&amp;#39;&amp;#39;&amp;#39; be moved if there is already
+a page at the new title, unless it is empty or a redirect and has no
+past edit history. This means that you can rename a page back to where
+it was just renamed from if you make a mistake, and you cannot overwrite
+an existing page.
+
+&amp;lt;b&amp;gt;WARNING!&amp;lt;/b&amp;gt;
+This can be a drastic and unexpected change for a popular page;
+please be sure you understand the consequences of this before
+proceeding.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Movepagetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movetalk&amp;action=edit movetalk]&lt;br&gt;
+[[MediaWiki_talk:Movetalk|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Move &amp;quot;talk&amp;quot; page as well, if applicable.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Movetalk</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movethispage&amp;action=edit movethispage]&lt;br&gt;
+[[MediaWiki_talk:Movethispage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Move this page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Movethispage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mycontris&amp;action=edit mycontris]&lt;br&gt;
+[[MediaWiki_talk:Mycontris|Talk]]
+&lt;/td&gt;&lt;td&gt;
+My contributions
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mycontris</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mypage&amp;action=edit mypage]&lt;br&gt;
+[[MediaWiki_talk:Mypage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+My page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mypage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mytalk&amp;action=edit mytalk]&lt;br&gt;
+[[MediaWiki_talk:Mytalk|Talk]]
+&lt;/td&gt;&lt;td&gt;
+My talk
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Mytalk</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Navigation&amp;action=edit navigation]&lt;br&gt;
+[[MediaWiki_talk:Navigation|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Navigation
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Navigation</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nbytes&amp;action=edit nbytes]&lt;br&gt;
+[[MediaWiki_talk:Nbytes|Talk]]
+&lt;/td&gt;&lt;td&gt;
+$1 bytes
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nbytes</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nchanges&amp;action=edit nchanges]&lt;br&gt;
+[[MediaWiki_talk:Nchanges|Talk]]
+&lt;/td&gt;&lt;td&gt;
+$1 changes
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nchanges</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newarticle&amp;action=edit newarticle]&lt;br&gt;
+[[MediaWiki_talk:Newarticle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(New)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Newarticle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newarticletext&amp;action=edit newarticletext]&lt;br&gt;
+[[MediaWiki_talk:Newarticletext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You&amp;#39;ve followed a link to a page that doesn&amp;#39;t exist yet.
+To create the page, start typing in the box below
+(see the &amp;#91;&amp;#91;Wiktionary:Help&amp;#124;help page]] for more info).
+If you are here by mistake, just click your browser&amp;#39;s &amp;#39;&amp;#39;&amp;#39;back&amp;#39;&amp;#39;&amp;#39; button.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Newarticletext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newmessages&amp;action=edit newmessages]&lt;br&gt;
+[[MediaWiki_talk:Newmessages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You have $1.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Newmessages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newmessageslink&amp;action=edit newmessageslink]&lt;br&gt;
+[[MediaWiki_talk:Newmessageslink|Talk]]
+&lt;/td&gt;&lt;td&gt;
+new messages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Newmessageslink</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newpage&amp;action=edit newpage]&lt;br&gt;
+[[MediaWiki_talk:Newpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+New page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Newpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newpageletter&amp;action=edit newpageletter]&lt;br&gt;
+[[MediaWiki_talk:Newpageletter|Talk]]
+&lt;/td&gt;&lt;td&gt;
+N
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Newpageletter</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newpages&amp;action=edit newpages]&lt;br&gt;
+[[MediaWiki_talk:Newpages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+New pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Newpages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newpassword&amp;action=edit newpassword]&lt;br&gt;
+[[MediaWiki_talk:Newpassword|Talk]]
+&lt;/td&gt;&lt;td&gt;
+New password
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Newpassword</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newtitle&amp;action=edit newtitle]&lt;br&gt;
+[[MediaWiki_talk:Newtitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+To new title
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Newtitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newusersonly&amp;action=edit newusersonly]&lt;br&gt;
+[[MediaWiki_talk:Newusersonly|Talk]]
+&lt;/td&gt;&lt;td&gt;
+ (new users only)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Newusersonly</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Next&amp;action=edit next]&lt;br&gt;
+[[MediaWiki_talk:Next|Talk]]
+&lt;/td&gt;&lt;td&gt;
+next
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Next</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nextn&amp;action=edit nextn]&lt;br&gt;
+[[MediaWiki_talk:Nextn|Talk]]
+&lt;/td&gt;&lt;td&gt;
+next $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nextn</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nlinks&amp;action=edit nlinks]&lt;br&gt;
+[[MediaWiki_talk:Nlinks|Talk]]
+&lt;/td&gt;&lt;td&gt;
+$1 links
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nlinks</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noaffirmation&amp;action=edit noaffirmation]&lt;br&gt;
+[[MediaWiki_talk:Noaffirmation|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You must affirm that your upload does not violate
+any copyrights.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Noaffirmation</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noarticletext&amp;action=edit noarticletext]&lt;br&gt;
+[[MediaWiki_talk:Noarticletext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(There is currently no text in this page)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Noarticletext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noblockreason&amp;action=edit noblockreason]&lt;br&gt;
+[[MediaWiki_talk:Noblockreason|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You must supply a reason for the block.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Noblockreason</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noconnect&amp;action=edit noconnect]&lt;br&gt;
+[[MediaWiki_talk:Noconnect|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Sorry! The wiki is experiencing some technical difficulties, and cannot contact the database server.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Noconnect</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nocontribs&amp;action=edit nocontribs]&lt;br&gt;
+[[MediaWiki_talk:Nocontribs|Talk]]
+&lt;/td&gt;&lt;td&gt;
+No changes were found matching these criteria.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nocontribs</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nocookieslogin&amp;action=edit nocookieslogin]&lt;br&gt;
+[[MediaWiki_talk:Nocookieslogin|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary uses cookies to log in users. You have cookies disabled. Please enable them and try again.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nocookieslogin</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nocookiesnew&amp;action=edit nocookiesnew]&lt;br&gt;
+[[MediaWiki_talk:Nocookiesnew|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The user account was created, but you are not logged in. Wiktionary uses cookies to log in users. You have cookies disabled. Please enable them, then log in with your new username and password.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nocookiesnew</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nocreativecommons&amp;action=edit nocreativecommons]&lt;br&gt;
+[[MediaWiki_talk:Nocreativecommons|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Creative Commons RDF metadata disabled for this server.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nocreativecommons</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nodb&amp;action=edit nodb]&lt;br&gt;
+[[MediaWiki_talk:Nodb|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Could not select database $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nodb</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nodublincore&amp;action=edit nodublincore]&lt;br&gt;
+[[MediaWiki_talk:Nodublincore|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Dublin Core RDF metadata disabled for this server.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nodublincore</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noemail&amp;action=edit noemail]&lt;br&gt;
+[[MediaWiki_talk:Noemail|Talk]]
+&lt;/td&gt;&lt;td&gt;
+There is no e-mail address recorded for user &amp;quot;$1&amp;quot;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Noemail</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noemailtext&amp;action=edit noemailtext]&lt;br&gt;
+[[MediaWiki_talk:Noemailtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This user has not specified a valid e-mail address,
+or has chosen not to receive e-mail from other users.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Noemailtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noemailtitle&amp;action=edit noemailtitle]&lt;br&gt;
+[[MediaWiki_talk:Noemailtitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+No e-mail address
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Noemailtitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nogomatch&amp;action=edit nogomatch]&lt;br&gt;
+[[MediaWiki_talk:Nogomatch|Talk]]
+&lt;/td&gt;&lt;td&gt;
+No page with this exact title exists, trying full text search.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nogomatch</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nohistory&amp;action=edit nohistory]&lt;br&gt;
+[[MediaWiki_talk:Nohistory|Talk]]
+&lt;/td&gt;&lt;td&gt;
+There is no edit history for this page.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nohistory</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nolinkshere&amp;action=edit nolinkshere]&lt;br&gt;
+[[MediaWiki_talk:Nolinkshere|Talk]]
+&lt;/td&gt;&lt;td&gt;
+No pages link to here.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nolinkshere</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nolinkstoimage&amp;action=edit nolinkstoimage]&lt;br&gt;
+[[MediaWiki_talk:Nolinkstoimage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+There are no pages that link to this image.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nolinkstoimage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noname&amp;action=edit noname]&lt;br&gt;
+[[MediaWiki_talk:Noname|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You have not specified a valid user name.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Noname</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nonefound&amp;action=edit nonefound]&lt;br&gt;
+[[MediaWiki_talk:Nonefound|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;strong&amp;gt;Note&amp;lt;/strong&amp;gt;: unsuccessful searches are
+often caused by searching for common words like &amp;quot;have&amp;quot; and &amp;quot;from&amp;quot;,
+which are not indexed, or by specifying more than one search term (only pages
+containing all of the search terms will appear in the result).
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nonefound</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nospecialpagetext&amp;action=edit nospecialpagetext]&lt;br&gt;
+[[MediaWiki_talk:Nospecialpagetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You have requested a special page that is not
+recognized by the wiki.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nospecialpagetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nosuchaction&amp;action=edit nosuchaction]&lt;br&gt;
+[[MediaWiki_talk:Nosuchaction|Talk]]
+&lt;/td&gt;&lt;td&gt;
+No such action
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nosuchaction</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nosuchactiontext&amp;action=edit nosuchactiontext]&lt;br&gt;
+[[MediaWiki_talk:Nosuchactiontext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The action specified by the URL is not
+recognized by the wiki
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nosuchactiontext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nosuchspecialpage&amp;action=edit nosuchspecialpage]&lt;br&gt;
+[[MediaWiki_talk:Nosuchspecialpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+No such special page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nosuchspecialpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nosuchuser&amp;action=edit nosuchuser]&lt;br&gt;
+[[MediaWiki_talk:Nosuchuser|Talk]]
+&lt;/td&gt;&lt;td&gt;
+There is no user by the name &amp;quot;$1&amp;quot;.
+Check your spelling, or use the form below to create a new user account.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nosuchuser</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notacceptable&amp;action=edit notacceptable]&lt;br&gt;
+[[MediaWiki_talk:Notacceptable|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The wiki server can&amp;#39;t provide data in a format your client can read.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Notacceptable</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notanarticle&amp;action=edit notanarticle]&lt;br&gt;
+[[MediaWiki_talk:Notanarticle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Not a content page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Notanarticle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notargettext&amp;action=edit notargettext]&lt;br&gt;
+[[MediaWiki_talk:Notargettext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You have not specified a target page or user
+to perform this function on.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Notargettext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notargettitle&amp;action=edit notargettitle]&lt;br&gt;
+[[MediaWiki_talk:Notargettitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+No target
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Notargettitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Note&amp;action=edit note]&lt;br&gt;
+[[MediaWiki_talk:Note|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;strong&amp;gt;Note:&amp;lt;/strong&amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Note</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notextmatches&amp;action=edit notextmatches]&lt;br&gt;
+[[MediaWiki_talk:Notextmatches|Talk]]
+&lt;/td&gt;&lt;td&gt;
+No page text matches
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Notextmatches</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notitlematches&amp;action=edit notitlematches]&lt;br&gt;
+[[MediaWiki_talk:Notitlematches|Talk]]
+&lt;/td&gt;&lt;td&gt;
+No page title matches
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Notitlematches</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notloggedin&amp;action=edit notloggedin]&lt;br&gt;
+[[MediaWiki_talk:Notloggedin|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Not logged in
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Notloggedin</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nowatchlist&amp;action=edit nowatchlist]&lt;br&gt;
+[[MediaWiki_talk:Nowatchlist|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You have no items on your watchlist.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nowatchlist</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nowiki_sample&amp;action=edit nowiki_sample]&lt;br&gt;
+[[MediaWiki_talk:Nowiki_sample|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Insert non-formatted text here
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nowiki_sample</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nowiki_tip&amp;action=edit nowiki_tip]&lt;br&gt;
+[[MediaWiki_talk:Nowiki_tip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Ignore wiki formatting
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nowiki_tip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-category&amp;action=edit nstab-category]&lt;br&gt;
+[[MediaWiki_talk:Nstab-category|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Category
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nstab-category</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-help&amp;action=edit nstab-help]&lt;br&gt;
+[[MediaWiki_talk:Nstab-help|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Help
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nstab-help</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-image&amp;action=edit nstab-image]&lt;br&gt;
+[[MediaWiki_talk:Nstab-image|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Image
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nstab-image</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-main&amp;action=edit nstab-main]&lt;br&gt;
+[[MediaWiki_talk:Nstab-main|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Article
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nstab-main</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-media&amp;action=edit nstab-media]&lt;br&gt;
+[[MediaWiki_talk:Nstab-media|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Media
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nstab-media</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-mediawiki&amp;action=edit nstab-mediawiki]&lt;br&gt;
+[[MediaWiki_talk:Nstab-mediawiki|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Message
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nstab-mediawiki</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-special&amp;action=edit nstab-special]&lt;br&gt;
+[[MediaWiki_talk:Nstab-special|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Special
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nstab-special</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-template&amp;action=edit nstab-template]&lt;br&gt;
+[[MediaWiki_talk:Nstab-template|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Template
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nstab-template</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-user&amp;action=edit nstab-user]&lt;br&gt;
+[[MediaWiki_talk:Nstab-user|Talk]]
+&lt;/td&gt;&lt;td&gt;
+User page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nstab-user</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-wp&amp;action=edit nstab-wp]&lt;br&gt;
+[[MediaWiki_talk:Nstab-wp|Talk]]
+&lt;/td&gt;&lt;td&gt;
+About
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nstab-wp</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nviews&amp;action=edit nviews]&lt;br&gt;
+[[MediaWiki_talk:Nviews|Talk]]
+&lt;/td&gt;&lt;td&gt;
+$1 views
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Nviews</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ok&amp;action=edit ok]&lt;br&gt;
+[[MediaWiki_talk:Ok|Talk]]
+&lt;/td&gt;&lt;td&gt;
+OK
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ok</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Oldpassword&amp;action=edit oldpassword]&lt;br&gt;
+[[MediaWiki_talk:Oldpassword|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Old password
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Oldpassword</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Orig&amp;action=edit orig]&lt;br&gt;
+[[MediaWiki_talk:Orig|Talk]]
+&lt;/td&gt;&lt;td&gt;
+orig
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Orig</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Orphans&amp;action=edit orphans]&lt;br&gt;
+[[MediaWiki_talk:Orphans|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Orphaned pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Orphans</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Othercontribs&amp;action=edit othercontribs]&lt;br&gt;
+[[MediaWiki_talk:Othercontribs|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Based on work by $1.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Othercontribs</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Otherlanguages&amp;action=edit otherlanguages]&lt;br&gt;
+[[MediaWiki_talk:Otherlanguages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Other languages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Otherlanguages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Pagemovedsub&amp;action=edit pagemovedsub]&lt;br&gt;
+[[MediaWiki_talk:Pagemovedsub|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Move succeeded
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Pagemovedsub</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Pagemovedtext&amp;action=edit pagemovedtext]&lt;br&gt;
+[[MediaWiki_talk:Pagemovedtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Page &amp;quot;&amp;#91;&amp;#91;$1]]&amp;quot; moved to &amp;quot;&amp;#91;&amp;#91;$2]]&amp;quot;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Pagemovedtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Pagetitle&amp;action=edit pagetitle]&lt;br&gt;
+[[MediaWiki_talk:Pagetitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+$1 - Wiktionary
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Pagetitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Passwordremindertext&amp;action=edit passwordremindertext]&lt;br&gt;
+[[MediaWiki_talk:Passwordremindertext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Someone (probably you, from IP address $1)
+requested that we send you a new Wiktionary login password.
+The password for user &amp;quot;$2&amp;quot; is now &amp;quot;$3&amp;quot;.
+You should log in and change your password now.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Passwordremindertext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Passwordremindertitle&amp;action=edit passwordremindertitle]&lt;br&gt;
+[[MediaWiki_talk:Passwordremindertitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Password reminder from Wiktionary
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Passwordremindertitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Passwordsent&amp;action=edit passwordsent]&lt;br&gt;
+[[MediaWiki_talk:Passwordsent|Talk]]
+&lt;/td&gt;&lt;td&gt;
+A new password has been sent to the e-mail address
+registered for &amp;quot;$1&amp;quot;.
+Please log in again after you receive it.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Passwordsent</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Perfcached&amp;action=edit perfcached]&lt;br&gt;
+[[MediaWiki_talk:Perfcached|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The following data is cached and may not be completely up to date:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Perfcached</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Perfdisabled&amp;action=edit perfdisabled]&lt;br&gt;
+[[MediaWiki_talk:Perfdisabled|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Sorry! This feature has been temporarily disabled
+because it slows the database down to the point that no one can use
+the wiki.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Perfdisabled</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Perfdisabledsub&amp;action=edit perfdisabledsub]&lt;br&gt;
+[[MediaWiki_talk:Perfdisabledsub|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Here&amp;#39;s a saved copy from $1:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Perfdisabledsub</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Personaltools&amp;action=edit personaltools]&lt;br&gt;
+[[MediaWiki_talk:Personaltools|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Personal tools
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Personaltools</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Popularpages&amp;action=edit popularpages]&lt;br&gt;
+[[MediaWiki_talk:Popularpages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Popular pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Popularpages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Portal&amp;action=edit portal]&lt;br&gt;
+[[MediaWiki_talk:Portal|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Community portal
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Portal</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Portal-url&amp;action=edit portal-url]&lt;br&gt;
+[[MediaWiki_talk:Portal-url|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary:Community Portal
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Portal-url</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Postcomment&amp;action=edit postcomment]&lt;br&gt;
+[[MediaWiki_talk:Postcomment|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Post a comment
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Postcomment</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Poweredby&amp;action=edit poweredby]&lt;br&gt;
+[[MediaWiki_talk:Poweredby|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary is powered by &amp;#91;http&amp;#58;//www.mediawiki.org/ MediaWiki], an open source wiki engine.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Poweredby</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Powersearch&amp;action=edit powersearch]&lt;br&gt;
+[[MediaWiki_talk:Powersearch|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Search
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Powersearch</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Powersearchtext&amp;action=edit powersearchtext]&lt;br&gt;
+[[MediaWiki_talk:Powersearchtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+
+Search in namespaces :&amp;lt;br /&amp;gt;
+$1&amp;lt;br /&amp;gt;
+$2 List redirects &amp;amp;nbsp; Search for $3 $9
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Powersearchtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Preferences&amp;action=edit preferences]&lt;br&gt;
+[[MediaWiki_talk:Preferences|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Preferences
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Preferences</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefs-help-userdata&amp;action=edit prefs-help-userdata]&lt;br&gt;
+[[MediaWiki_talk:Prefs-help-userdata|Talk]]
+&lt;/td&gt;&lt;td&gt;
+* &amp;lt;strong&amp;gt;Real name&amp;lt;/strong&amp;gt; (optional): if you choose to provide it this will be used for giving you attribution for your work.&amp;lt;br/&amp;gt;
+* &amp;lt;strong&amp;gt;Email&amp;lt;/strong&amp;gt; (optional): Enables people to contact you through the website without you having to reveal your
+email address to them, and it can be used to send you a new password if you forget it.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Prefs-help-userdata</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefs-misc&amp;action=edit prefs-misc]&lt;br&gt;
+[[MediaWiki_talk:Prefs-misc|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Misc settings
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Prefs-misc</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefs-personal&amp;action=edit prefs-personal]&lt;br&gt;
+[[MediaWiki_talk:Prefs-personal|Talk]]
+&lt;/td&gt;&lt;td&gt;
+User data
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Prefs-personal</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefs-rc&amp;action=edit prefs-rc]&lt;br&gt;
+[[MediaWiki_talk:Prefs-rc|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Recent changes and stub display
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Prefs-rc</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefslogintext&amp;action=edit prefslogintext]&lt;br&gt;
+[[MediaWiki_talk:Prefslogintext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You are logged in as &amp;quot;$1&amp;quot;.
+Your internal ID number is $2.
+
+See &amp;#91;&amp;#91;Wiktionary:User preferences help]] for help deciphering the options.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Prefslogintext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefsnologin&amp;action=edit prefsnologin]&lt;br&gt;
+[[MediaWiki_talk:Prefsnologin|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Not logged in
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Prefsnologin</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefsnologintext&amp;action=edit prefsnologintext]&lt;br&gt;
+[[MediaWiki_talk:Prefsnologintext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You must be &amp;lt;a href=&amp;quot;/wiki/Special:Userlogin&amp;quot;&amp;gt;logged in&amp;lt;/a&amp;gt;
+to set user preferences.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Prefsnologintext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefsreset&amp;action=edit prefsreset]&lt;br&gt;
+[[MediaWiki_talk:Prefsreset|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Preferences have been reset from storage.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Prefsreset</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Preview&amp;action=edit preview]&lt;br&gt;
+[[MediaWiki_talk:Preview|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Preview
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Preview</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Previewconflict&amp;action=edit previewconflict]&lt;br&gt;
+[[MediaWiki_talk:Previewconflict|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This preview reflects the text in the upper
+text editing area as it will appear if you choose to save.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Previewconflict</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Previewnote&amp;action=edit previewnote]&lt;br&gt;
+[[MediaWiki_talk:Previewnote|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Remember that this is only a preview, and has not yet been saved!
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Previewnote</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prevn&amp;action=edit prevn]&lt;br&gt;
+[[MediaWiki_talk:Prevn|Talk]]
+&lt;/td&gt;&lt;td&gt;
+previous $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Prevn</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Printableversion&amp;action=edit printableversion]&lt;br&gt;
+[[MediaWiki_talk:Printableversion|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Printable version
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Printableversion</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Printsubtitle&amp;action=edit printsubtitle]&lt;br&gt;
+[[MediaWiki_talk:Printsubtitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(From http&amp;#58;//tl.wiktionary.org)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Printsubtitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protect&amp;action=edit protect]&lt;br&gt;
+[[MediaWiki_talk:Protect|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Protect
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Protect</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectcomment&amp;action=edit protectcomment]&lt;br&gt;
+[[MediaWiki_talk:Protectcomment|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Reason for protecting
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Protectcomment</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectedarticle&amp;action=edit protectedarticle]&lt;br&gt;
+[[MediaWiki_talk:Protectedarticle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+protected &amp;#91;&amp;#91;$1]]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Protectedarticle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectedpage&amp;action=edit protectedpage]&lt;br&gt;
+[[MediaWiki_talk:Protectedpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Protected page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Protectedpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectedpagewarning&amp;action=edit protectedpagewarning]&lt;br&gt;
+[[MediaWiki_talk:Protectedpagewarning|Talk]]
+&lt;/td&gt;&lt;td&gt;
+WARNING: This page has been locked so that only
+users with sysop privileges can edit it. Be sure you are following the
+&amp;lt;a href=&amp;#39;/w/wiki.phtml/Wiktionary:Protected_page_guidelines&amp;#39;&amp;gt;protected page
+guidelines&amp;lt;/a&amp;gt;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Protectedpagewarning</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectedtext&amp;action=edit protectedtext]&lt;br&gt;
+[[MediaWiki_talk:Protectedtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This page has been locked to prevent editing; there are
+a number of reasons why this may be so, please see
+&amp;#91;&amp;#91;Wiktionary:Protected page]].
+
+You can view and copy the source of this page:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Protectedtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectlogpage&amp;action=edit protectlogpage]&lt;br&gt;
+[[MediaWiki_talk:Protectlogpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Protection_log
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Protectlogpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectlogtext&amp;action=edit protectlogtext]&lt;br&gt;
+[[MediaWiki_talk:Protectlogtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Below is a list of page locks/unlocks.
+See &amp;#91;&amp;#91;Wiktionary:Protected page]] for more information.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Protectlogtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectpage&amp;action=edit protectpage]&lt;br&gt;
+[[MediaWiki_talk:Protectpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Protect page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Protectpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectreason&amp;action=edit protectreason]&lt;br&gt;
+[[MediaWiki_talk:Protectreason|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(give a reason)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Protectreason</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectsub&amp;action=edit protectsub]&lt;br&gt;
+[[MediaWiki_talk:Protectsub|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(Protecting &amp;quot;$1&amp;quot;)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Protectsub</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectthispage&amp;action=edit protectthispage]&lt;br&gt;
+[[MediaWiki_talk:Protectthispage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Protect this page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Protectthispage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Proxyblocker&amp;action=edit proxyblocker]&lt;br&gt;
+[[MediaWiki_talk:Proxyblocker|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Proxy blocker
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Proxyblocker</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Proxyblockreason&amp;action=edit proxyblockreason]&lt;br&gt;
+[[MediaWiki_talk:Proxyblockreason|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Your IP address has been blocked because it is an open proxy. Please contact your Internet service provider or tech support and inform them of this serious security problem.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Proxyblockreason</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Proxyblocksuccess&amp;action=edit proxyblocksuccess]&lt;br&gt;
+[[MediaWiki_talk:Proxyblocksuccess|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Done.
+
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Proxyblocksuccess</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbbrowse&amp;action=edit qbbrowse]&lt;br&gt;
+[[MediaWiki_talk:Qbbrowse|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Browse
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Qbbrowse</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbedit&amp;action=edit qbedit]&lt;br&gt;
+[[MediaWiki_talk:Qbedit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Edit
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Qbedit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbfind&amp;action=edit qbfind]&lt;br&gt;
+[[MediaWiki_talk:Qbfind|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Find
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Qbfind</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbmyoptions&amp;action=edit qbmyoptions]&lt;br&gt;
+[[MediaWiki_talk:Qbmyoptions|Talk]]
+&lt;/td&gt;&lt;td&gt;
+My pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Qbmyoptions</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbpageinfo&amp;action=edit qbpageinfo]&lt;br&gt;
+[[MediaWiki_talk:Qbpageinfo|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Context
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Qbpageinfo</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbpageoptions&amp;action=edit qbpageoptions]&lt;br&gt;
+[[MediaWiki_talk:Qbpageoptions|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Qbpageoptions</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbsettings&amp;action=edit qbsettings]&lt;br&gt;
+[[MediaWiki_talk:Qbsettings|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Quickbar settings
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Qbsettings</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbspecialpages&amp;action=edit qbspecialpages]&lt;br&gt;
+[[MediaWiki_talk:Qbspecialpages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Special pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Qbspecialpages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Querybtn&amp;action=edit querybtn]&lt;br&gt;
+[[MediaWiki_talk:Querybtn|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Submit query
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Querybtn</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Querysuccessful&amp;action=edit querysuccessful]&lt;br&gt;
+[[MediaWiki_talk:Querysuccessful|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Query successful
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Querysuccessful</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Randompage&amp;action=edit randompage]&lt;br&gt;
+[[MediaWiki_talk:Randompage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Random page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Randompage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Range_block_disabled&amp;action=edit range_block_disabled]&lt;br&gt;
+[[MediaWiki_talk:Range_block_disabled|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The sysop ability to create range blocks is disabled.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Range_block_disabled</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rchide&amp;action=edit rchide]&lt;br&gt;
+[[MediaWiki_talk:Rchide|Talk]]
+&lt;/td&gt;&lt;td&gt;
+in $4 form; $1 minor edits; $2 secondary namespaces; $3 multiple edits.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rchide</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rclinks&amp;action=edit rclinks]&lt;br&gt;
+[[MediaWiki_talk:Rclinks|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Show last $1 changes in last $2 days&amp;lt;br /&amp;gt;$3
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rclinks</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rclistfrom&amp;action=edit rclistfrom]&lt;br&gt;
+[[MediaWiki_talk:Rclistfrom|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Show new changes starting from $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rclistfrom</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rcliu&amp;action=edit rcliu]&lt;br&gt;
+[[MediaWiki_talk:Rcliu|Talk]]
+&lt;/td&gt;&lt;td&gt;
+; $1 edits from logged in users
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rcliu</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rcloaderr&amp;action=edit rcloaderr]&lt;br&gt;
+[[MediaWiki_talk:Rcloaderr|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Loading recent changes
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rcloaderr</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rclsub&amp;action=edit rclsub]&lt;br&gt;
+[[MediaWiki_talk:Rclsub|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(to pages linked from &amp;quot;$1&amp;quot;)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rclsub</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rcnote&amp;action=edit rcnote]&lt;br&gt;
+[[MediaWiki_talk:Rcnote|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Below are the last &amp;lt;strong&amp;gt;$1&amp;lt;/strong&amp;gt; changes in last &amp;lt;strong&amp;gt;$2&amp;lt;/strong&amp;gt; days.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rcnote</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rcnotefrom&amp;action=edit rcnotefrom]&lt;br&gt;
+[[MediaWiki_talk:Rcnotefrom|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Below are the changes since &amp;lt;b&amp;gt;$2&amp;lt;/b&amp;gt; (up to &amp;lt;b&amp;gt;$1&amp;lt;/b&amp;gt; shown).
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rcnotefrom</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Readonly&amp;action=edit readonly]&lt;br&gt;
+[[MediaWiki_talk:Readonly|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Database locked
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Readonly</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Readonlytext&amp;action=edit readonlytext]&lt;br&gt;
+[[MediaWiki_talk:Readonlytext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The database is currently locked to new
+entries and other modifications, probably for routine database maintenance,
+after which it will be back to normal.
+The administrator who locked it offered this explanation:
+&amp;lt;p&amp;gt;$1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Readonlytext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Readonlywarning&amp;action=edit readonlywarning]&lt;br&gt;
+[[MediaWiki_talk:Readonlywarning|Talk]]
+&lt;/td&gt;&lt;td&gt;
+WARNING: The database has been locked for maintenance,
+so you will not be able to save your edits right now. You may wish to cut-n-paste
+the text into a text file and save it for later.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Readonlywarning</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Recentchanges&amp;action=edit recentchanges]&lt;br&gt;
+[[MediaWiki_talk:Recentchanges|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Recent changes
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Recentchanges</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Recentchangescount&amp;action=edit recentchangescount]&lt;br&gt;
+[[MediaWiki_talk:Recentchangescount|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Number of titles in recent changes
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Recentchangescount</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Recentchangeslinked&amp;action=edit recentchangeslinked]&lt;br&gt;
+[[MediaWiki_talk:Recentchangeslinked|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Related changes
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Recentchangeslinked</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Recentchangestext&amp;action=edit recentchangestext]&lt;br&gt;
+[[MediaWiki_talk:Recentchangestext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Track the most recent changes to the wiki on this page.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Recentchangestext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Redirectedfrom&amp;action=edit redirectedfrom]&lt;br&gt;
+[[MediaWiki_talk:Redirectedfrom|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(Redirected from $1)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Redirectedfrom</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Remembermypassword&amp;action=edit remembermypassword]&lt;br&gt;
+[[MediaWiki_talk:Remembermypassword|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Remember my password across sessions.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Remembermypassword</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Removechecked&amp;action=edit removechecked]&lt;br&gt;
+[[MediaWiki_talk:Removechecked|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Remove checked items from watchlist
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Removechecked</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Removedwatch&amp;action=edit removedwatch]&lt;br&gt;
+[[MediaWiki_talk:Removedwatch|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Removed from watchlist
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Removedwatch</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Removedwatchtext&amp;action=edit removedwatchtext]&lt;br&gt;
+[[MediaWiki_talk:Removedwatchtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The page &amp;quot;$1&amp;quot; has been removed from your watchlist.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Removedwatchtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Removingchecked&amp;action=edit removingchecked]&lt;br&gt;
+[[MediaWiki_talk:Removingchecked|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Removing requested items from watchlist...
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Removingchecked</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Resetprefs&amp;action=edit resetprefs]&lt;br&gt;
+[[MediaWiki_talk:Resetprefs|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Reset preferences
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Resetprefs</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Restorelink&amp;action=edit restorelink]&lt;br&gt;
+[[MediaWiki_talk:Restorelink|Talk]]
+&lt;/td&gt;&lt;td&gt;
+$1 deleted edits
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Restorelink</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Resultsperpage&amp;action=edit resultsperpage]&lt;br&gt;
+[[MediaWiki_talk:Resultsperpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Hits to show per page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Resultsperpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Retrievedfrom&amp;action=edit retrievedfrom]&lt;br&gt;
+[[MediaWiki_talk:Retrievedfrom|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Retrieved from &amp;quot;$1&amp;quot;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Retrievedfrom</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Returnto&amp;action=edit returnto]&lt;br&gt;
+[[MediaWiki_talk:Returnto|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Return to $1.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Returnto</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Retypenew&amp;action=edit retypenew]&lt;br&gt;
+[[MediaWiki_talk:Retypenew|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Retype new password
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Retypenew</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Reupload&amp;action=edit reupload]&lt;br&gt;
+[[MediaWiki_talk:Reupload|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Re-upload
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Reupload</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Reuploaddesc&amp;action=edit reuploaddesc]&lt;br&gt;
+[[MediaWiki_talk:Reuploaddesc|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Return to the upload form.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Reuploaddesc</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Reverted&amp;action=edit reverted]&lt;br&gt;
+[[MediaWiki_talk:Reverted|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Reverted to earlier revision
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Reverted</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Revertimg&amp;action=edit revertimg]&lt;br&gt;
+[[MediaWiki_talk:Revertimg|Talk]]
+&lt;/td&gt;&lt;td&gt;
+rev
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Revertimg</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Revertpage&amp;action=edit revertpage]&lt;br&gt;
+[[MediaWiki_talk:Revertpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Reverted edit of $2, changed back to last version by $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Revertpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Revhistory&amp;action=edit revhistory]&lt;br&gt;
+[[MediaWiki_talk:Revhistory|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Revision history
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Revhistory</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Revisionasof&amp;action=edit revisionasof]&lt;br&gt;
+[[MediaWiki_talk:Revisionasof|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Revision as of $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Revisionasof</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Revnotfound&amp;action=edit revnotfound]&lt;br&gt;
+[[MediaWiki_talk:Revnotfound|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Revision not found
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Revnotfound</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Revnotfoundtext&amp;action=edit revnotfoundtext]&lt;br&gt;
+[[MediaWiki_talk:Revnotfoundtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The old revision of the page you asked for could not be found.
+Please check the URL you used to access this page.
+
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Revnotfoundtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rfcurl&amp;action=edit rfcurl]&lt;br&gt;
+[[MediaWiki_talk:Rfcurl|Talk]]
+&lt;/td&gt;&lt;td&gt;
+http&amp;#58;//www.faqs.org/rfcs/rfc$1.html
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rfcurl</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rights&amp;action=edit rights]&lt;br&gt;
+[[MediaWiki_talk:Rights|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Rights:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rights</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rollback&amp;action=edit rollback]&lt;br&gt;
+[[MediaWiki_talk:Rollback|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Roll back edits
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rollback</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rollback_short&amp;action=edit rollback_short]&lt;br&gt;
+[[MediaWiki_talk:Rollback_short|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Rollback
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rollback_short</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rollbackfailed&amp;action=edit rollbackfailed]&lt;br&gt;
+[[MediaWiki_talk:Rollbackfailed|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Rollback failed
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rollbackfailed</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rollbacklink&amp;action=edit rollbacklink]&lt;br&gt;
+[[MediaWiki_talk:Rollbacklink|Talk]]
+&lt;/td&gt;&lt;td&gt;
+rollback
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rollbacklink</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rows&amp;action=edit rows]&lt;br&gt;
+[[MediaWiki_talk:Rows|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Rows
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Rows</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Savearticle&amp;action=edit savearticle]&lt;br&gt;
+[[MediaWiki_talk:Savearticle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Save page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Savearticle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Savedprefs&amp;action=edit savedprefs]&lt;br&gt;
+[[MediaWiki_talk:Savedprefs|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Your preferences have been saved.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Savedprefs</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Savefile&amp;action=edit savefile]&lt;br&gt;
+[[MediaWiki_talk:Savefile|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Save file
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Savefile</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Saveprefs&amp;action=edit saveprefs]&lt;br&gt;
+[[MediaWiki_talk:Saveprefs|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Save preferences
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Saveprefs</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Search&amp;action=edit search]&lt;br&gt;
+[[MediaWiki_talk:Search|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Search
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Search</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchdisabled&amp;action=edit searchdisabled]&lt;br&gt;
+[[MediaWiki_talk:Searchdisabled|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;p&amp;gt;Sorry! Full text search has been disabled temporarily, for performance reasons. In the meantime, you can use the Google search below, which may be out of date.&amp;lt;/p&amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Searchdisabled</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchhelppage&amp;action=edit searchhelppage]&lt;br&gt;
+[[MediaWiki_talk:Searchhelppage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary:Searching
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Searchhelppage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchingwikipedia&amp;action=edit searchingwikipedia]&lt;br&gt;
+[[MediaWiki_talk:Searchingwikipedia|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Searching Wiktionary
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Searchingwikipedia</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchquery&amp;action=edit searchquery]&lt;br&gt;
+[[MediaWiki_talk:Searchquery|Talk]]
+&lt;/td&gt;&lt;td&gt;
+For query &amp;quot;$1&amp;quot;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Searchquery</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchresults&amp;action=edit searchresults]&lt;br&gt;
+[[MediaWiki_talk:Searchresults|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Search results
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Searchresults</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchresultshead&amp;action=edit searchresultshead]&lt;br&gt;
+[[MediaWiki_talk:Searchresultshead|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Search result settings
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Searchresultshead</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchresulttext&amp;action=edit searchresulttext]&lt;br&gt;
+[[MediaWiki_talk:Searchresulttext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+For more information about searching Wiktionary, see $1.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Searchresulttext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sectionedit&amp;action=edit sectionedit]&lt;br&gt;
+[[MediaWiki_talk:Sectionedit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+ (section)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Sectionedit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Selectnewerversionfordiff&amp;action=edit selectnewerversionfordiff]&lt;br&gt;
+[[MediaWiki_talk:Selectnewerversionfordiff|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Select a newer version for comparison
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Selectnewerversionfordiff</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Selectolderversionfordiff&amp;action=edit selectolderversionfordiff]&lt;br&gt;
+[[MediaWiki_talk:Selectolderversionfordiff|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Select an older version for comparison
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Selectolderversionfordiff</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Selectonly&amp;action=edit selectonly]&lt;br&gt;
+[[MediaWiki_talk:Selectonly|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Only read-only queries are allowed.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Selectonly</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Selflinks&amp;action=edit selflinks]&lt;br&gt;
+[[MediaWiki_talk:Selflinks|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Pages with Self Links
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Selflinks</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Selflinkstext&amp;action=edit selflinkstext]&lt;br&gt;
+[[MediaWiki_talk:Selflinkstext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The following pages contain a link to themselves, which they should not.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Selflinkstext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Seriousxhtmlerrors&amp;action=edit seriousxhtmlerrors]&lt;br&gt;
+[[MediaWiki_talk:Seriousxhtmlerrors|Talk]]
+&lt;/td&gt;&lt;td&gt;
+There were serious xhtml markup errors detected by tidy.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Seriousxhtmlerrors</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Servertime&amp;action=edit servertime]&lt;br&gt;
+[[MediaWiki_talk:Servertime|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Server time is now
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Servertime</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Set_rights_fail&amp;action=edit set_rights_fail]&lt;br&gt;
+[[MediaWiki_talk:Set_rights_fail|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;b&amp;gt;User rights for &amp;quot;$1&amp;quot; could not be set. (Did you enter the name correctly?)&amp;lt;/b&amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Set_rights_fail</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Set_user_rights&amp;action=edit set_user_rights]&lt;br&gt;
+[[MediaWiki_talk:Set_user_rights|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Set user rights
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Set_user_rights</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Setbureaucratflag&amp;action=edit setbureaucratflag]&lt;br&gt;
+[[MediaWiki_talk:Setbureaucratflag|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Set bureaucrat flag
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Setbureaucratflag</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Shortpages&amp;action=edit shortpages]&lt;br&gt;
+[[MediaWiki_talk:Shortpages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Short pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Shortpages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Show&amp;action=edit show]&lt;br&gt;
+[[MediaWiki_talk:Show|Talk]]
+&lt;/td&gt;&lt;td&gt;
+show
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Show</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Showhideminor&amp;action=edit showhideminor]&lt;br&gt;
+[[MediaWiki_talk:Showhideminor|Talk]]
+&lt;/td&gt;&lt;td&gt;
+$1 minor edits &amp;#124; $2 bots &amp;#124; $3 logged in users
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Showhideminor</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Showingresults&amp;action=edit showingresults]&lt;br&gt;
+[[MediaWiki_talk:Showingresults|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Showing below &amp;lt;b&amp;gt;$1&amp;lt;/b&amp;gt; results starting with #&amp;lt;b&amp;gt;$2&amp;lt;/b&amp;gt;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Showingresults</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Showingresultsnum&amp;action=edit showingresultsnum]&lt;br&gt;
+[[MediaWiki_talk:Showingresultsnum|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Showing below &amp;lt;b&amp;gt;$3&amp;lt;/b&amp;gt; results starting with #&amp;lt;b&amp;gt;$2&amp;lt;/b&amp;gt;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Showingresultsnum</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Showlast&amp;action=edit showlast]&lt;br&gt;
+[[MediaWiki_talk:Showlast|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Show last $1 images sorted $2.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Showlast</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Showpreview&amp;action=edit showpreview]&lt;br&gt;
+[[MediaWiki_talk:Showpreview|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Show preview
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Showpreview</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Showtoc&amp;action=edit showtoc]&lt;br&gt;
+[[MediaWiki_talk:Showtoc|Talk]]
+&lt;/td&gt;&lt;td&gt;
+show
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Showtoc</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sig_tip&amp;action=edit sig_tip]&lt;br&gt;
+[[MediaWiki_talk:Sig_tip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Your signature with timestamp
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Sig_tip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sitestats&amp;action=edit sitestats]&lt;br&gt;
+[[MediaWiki_talk:Sitestats|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Site statistics
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Sitestats</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sitestatstext&amp;action=edit sitestatstext]&lt;br&gt;
+[[MediaWiki_talk:Sitestatstext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+There are &amp;#39;&amp;#39;&amp;#39;$1&amp;#39;&amp;#39;&amp;#39; total pages in the database.
+This includes &amp;quot;talk&amp;quot; pages, pages about Wiktionary, minimal &amp;quot;stub&amp;quot;
+pages, redirects, and others that probably don&amp;#39;t qualify as content pages.
+Excluding those, there are &amp;#39;&amp;#39;&amp;#39;$2&amp;#39;&amp;#39;&amp;#39; pages that are probably legitimate
+content pages.
+
+There have been a total of &amp;#39;&amp;#39;&amp;#39;$3&amp;#39;&amp;#39;&amp;#39; page views, and &amp;#39;&amp;#39;&amp;#39;$4&amp;#39;&amp;#39;&amp;#39; page edits
+since the wiki was setup.
+That comes to &amp;#39;&amp;#39;&amp;#39;$5&amp;#39;&amp;#39;&amp;#39; average edits per page, and &amp;#39;&amp;#39;&amp;#39;$6&amp;#39;&amp;#39;&amp;#39; views per edit.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Sitestatstext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sitesubtitle&amp;action=edit sitesubtitle]&lt;br&gt;
+[[MediaWiki_talk:Sitesubtitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The Free Encyclopedia
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Sitesubtitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sitesupport&amp;action=edit sitesupport]&lt;br&gt;
+[[MediaWiki_talk:Sitesupport|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Donations
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Sitesupport</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sitetitle&amp;action=edit sitetitle]&lt;br&gt;
+[[MediaWiki_talk:Sitetitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Sitetitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Siteuser&amp;action=edit siteuser]&lt;br&gt;
+[[MediaWiki_talk:Siteuser|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary user $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Siteuser</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Siteusers&amp;action=edit siteusers]&lt;br&gt;
+[[MediaWiki_talk:Siteusers|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary user(s) $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Siteusers</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Skin&amp;action=edit skin]&lt;br&gt;
+[[MediaWiki_talk:Skin|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Skin
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Skin</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Spamprotectiontext&amp;action=edit spamprotectiontext]&lt;br&gt;
+[[MediaWiki_talk:Spamprotectiontext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The page you wanted to save was blocked by the spam filter. This is probably caused by a link to an external site.
+
+You might want to check the following regular expression for patterns that are currently blocked:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Spamprotectiontext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Spamprotectiontitle&amp;action=edit spamprotectiontitle]&lt;br&gt;
+[[MediaWiki_talk:Spamprotectiontitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Spam protection filter
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Spamprotectiontitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Specialpage&amp;action=edit specialpage]&lt;br&gt;
+[[MediaWiki_talk:Specialpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Special Page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Specialpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Specialpages&amp;action=edit specialpages]&lt;br&gt;
+[[MediaWiki_talk:Specialpages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Special pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Specialpages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Spheading&amp;action=edit spheading]&lt;br&gt;
+[[MediaWiki_talk:Spheading|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Special pages for all users
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Spheading</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sqlislogged&amp;action=edit sqlislogged]&lt;br&gt;
+[[MediaWiki_talk:Sqlislogged|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Please note that all queries are logged.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Sqlislogged</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sqlquery&amp;action=edit sqlquery]&lt;br&gt;
+[[MediaWiki_talk:Sqlquery|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Enter query
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Sqlquery</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Statistics&amp;action=edit statistics]&lt;br&gt;
+[[MediaWiki_talk:Statistics|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Statistics
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Statistics</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Storedversion&amp;action=edit storedversion]&lt;br&gt;
+[[MediaWiki_talk:Storedversion|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Stored version
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Storedversion</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Stubthreshold&amp;action=edit stubthreshold]&lt;br&gt;
+[[MediaWiki_talk:Stubthreshold|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Threshold for stub display
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Stubthreshold</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Subcategories&amp;action=edit subcategories]&lt;br&gt;
+[[MediaWiki_talk:Subcategories|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Subcategories
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Subcategories</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Subject&amp;action=edit subject]&lt;br&gt;
+[[MediaWiki_talk:Subject|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Subject/headline
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Subject</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Subjectpage&amp;action=edit subjectpage]&lt;br&gt;
+[[MediaWiki_talk:Subjectpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View subject
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Subjectpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Successfulupload&amp;action=edit successfulupload]&lt;br&gt;
+[[MediaWiki_talk:Successfulupload|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Successful upload
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Successfulupload</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Summary&amp;action=edit summary]&lt;br&gt;
+[[MediaWiki_talk:Summary|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Summary
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Summary</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sysopspheading&amp;action=edit sysopspheading]&lt;br&gt;
+[[MediaWiki_talk:Sysopspheading|Talk]]
+&lt;/td&gt;&lt;td&gt;
+For sysop use only
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Sysopspheading</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sysoptext&amp;action=edit sysoptext]&lt;br&gt;
+[[MediaWiki_talk:Sysoptext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The action you have requested can only be
+performed by users with &amp;quot;sysop&amp;quot; status.
+See $1.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Sysoptext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sysoptitle&amp;action=edit sysoptitle]&lt;br&gt;
+[[MediaWiki_talk:Sysoptitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Sysop access required
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Sysoptitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tableform&amp;action=edit tableform]&lt;br&gt;
+[[MediaWiki_talk:Tableform|Talk]]
+&lt;/td&gt;&lt;td&gt;
+table
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tableform</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Talk&amp;action=edit talk]&lt;br&gt;
+[[MediaWiki_talk:Talk|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Discussion
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Talk</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Talkexists&amp;action=edit talkexists]&lt;br&gt;
+[[MediaWiki_talk:Talkexists|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The page itself was moved successfully, but the
+talk page could not be moved because one already exists at the new
+title. Please merge them manually.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Talkexists</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Talkpage&amp;action=edit talkpage]&lt;br&gt;
+[[MediaWiki_talk:Talkpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Discuss this page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Talkpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Talkpagemoved&amp;action=edit talkpagemoved]&lt;br&gt;
+[[MediaWiki_talk:Talkpagemoved|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The corresponding talk page was also moved.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Talkpagemoved</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Talkpagenotmoved&amp;action=edit talkpagenotmoved]&lt;br&gt;
+[[MediaWiki_talk:Talkpagenotmoved|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The corresponding talk page was &amp;lt;strong&amp;gt;not&amp;lt;/strong&amp;gt; moved.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Talkpagenotmoved</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Talkpagetext&amp;action=edit talkpagetext]&lt;br&gt;
+[[MediaWiki_talk:Talkpagetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;!-- MediaWiki:talkpagetext --&amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Talkpagetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Textboxsize&amp;action=edit textboxsize]&lt;br&gt;
+[[MediaWiki_talk:Textboxsize|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Textbox dimensions
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Textboxsize</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Textmatches&amp;action=edit textmatches]&lt;br&gt;
+[[MediaWiki_talk:Textmatches|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Page text matches
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Textmatches</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Thisisdeleted&amp;action=edit thisisdeleted]&lt;br&gt;
+[[MediaWiki_talk:Thisisdeleted|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View or restore $1?
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Thisisdeleted</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Thumbnail-more&amp;action=edit thumbnail-more]&lt;br&gt;
+[[MediaWiki_talk:Thumbnail-more|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Enlarge
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Thumbnail-more</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Timezonelegend&amp;action=edit timezonelegend]&lt;br&gt;
+[[MediaWiki_talk:Timezonelegend|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Time zone
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Timezonelegend</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Timezoneoffset&amp;action=edit timezoneoffset]&lt;br&gt;
+[[MediaWiki_talk:Timezoneoffset|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Offset
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Timezoneoffset</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Timezonetext&amp;action=edit timezonetext]&lt;br&gt;
+[[MediaWiki_talk:Timezonetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Enter number of hours your local time differs
+from server time (UTC).
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Timezonetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Titlematches&amp;action=edit titlematches]&lt;br&gt;
+[[MediaWiki_talk:Titlematches|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Article title matches
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Titlematches</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Toc&amp;action=edit toc]&lt;br&gt;
+[[MediaWiki_talk:Toc|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Table of contents
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Toc</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Toolbox&amp;action=edit toolbox]&lt;br&gt;
+[[MediaWiki_talk:Toolbox|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Toolbox
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Toolbox</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-addsection&amp;action=edit tooltip-addsection]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-addsection|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Add a comment to this page. &amp;#91;alt-+]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-addsection</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-anontalk&amp;action=edit tooltip-anontalk]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-anontalk|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Discussion about edits from this ip address &amp;#91;alt-n]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-anontalk</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-anonuserpage&amp;action=edit tooltip-anonuserpage]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-anonuserpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The user page for the ip you&amp;#39;re editing as &amp;#91;alt-.]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-anonuserpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-article&amp;action=edit tooltip-article]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-article|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View the content page &amp;#91;alt-a]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-article</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-atom&amp;action=edit tooltip-atom]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-atom|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Atom feed for this page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-atom</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-compareselectedversions&amp;action=edit tooltip-compareselectedversions]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-compareselectedversions|Talk]]
+&lt;/td&gt;&lt;td&gt;
+See the differences between the two selected versions of this page. &amp;#91;alt-v]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-compareselectedversions</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-contributions&amp;action=edit tooltip-contributions]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-contributions|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View the list of contributions of this user
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-contributions</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-currentevents&amp;action=edit tooltip-currentevents]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-currentevents|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Find background information on current events
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-currentevents</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-delete&amp;action=edit tooltip-delete]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-delete|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Delete this page &amp;#91;alt-d]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-delete</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-edit&amp;action=edit tooltip-edit]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-edit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You can edit this page. Please use the preview button before saving. &amp;#91;alt-e]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-edit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-emailuser&amp;action=edit tooltip-emailuser]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-emailuser|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Send a mail to this user
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-emailuser</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-help&amp;action=edit tooltip-help]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-help|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The place to find out.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-help</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-history&amp;action=edit tooltip-history]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-history|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Past versions of this page, &amp;#91;alt-h]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-history</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-login&amp;action=edit tooltip-login]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-login|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You are encouraged to log in, it is not mandatory however. &amp;#91;alt-o]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-login</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-logout&amp;action=edit tooltip-logout]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-logout|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Log out &amp;#91;alt-o]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-logout</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-mainpage&amp;action=edit tooltip-mainpage]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-mainpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Visit the Main Page &amp;#91;alt-z]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-mainpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-minoredit&amp;action=edit tooltip-minoredit]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-minoredit|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Mark this as a minor edit &amp;#91;alt-i]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-minoredit</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-move&amp;action=edit tooltip-move]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-move|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Move this page &amp;#91;alt-m]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-move</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-mycontris&amp;action=edit tooltip-mycontris]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-mycontris|Talk]]
+&lt;/td&gt;&lt;td&gt;
+List of my contributions &amp;#91;alt-y]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-mycontris</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-mytalk&amp;action=edit tooltip-mytalk]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-mytalk|Talk]]
+&lt;/td&gt;&lt;td&gt;
+My talk page &amp;#91;alt-n]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-mytalk</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-nomove&amp;action=edit tooltip-nomove]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-nomove|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You don&amp;#39;t have the permissions to move this page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-nomove</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-portal&amp;action=edit tooltip-portal]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-portal|Talk]]
+&lt;/td&gt;&lt;td&gt;
+About the project, what you can do, where to find things
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-portal</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-preferences&amp;action=edit tooltip-preferences]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-preferences|Talk]]
+&lt;/td&gt;&lt;td&gt;
+My preferences
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-preferences</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-preview&amp;action=edit tooltip-preview]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-preview|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Preview your changes, please use this before saving! &amp;#91;alt-p]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-preview</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-protect&amp;action=edit tooltip-protect]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-protect|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Protect this page &amp;#91;alt-=]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-protect</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-randompage&amp;action=edit tooltip-randompage]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-randompage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Load a random page &amp;#91;alt-x]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-randompage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-recentchanges&amp;action=edit tooltip-recentchanges]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-recentchanges|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The list of recent changes in the wiki. &amp;#91;alt-r]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-recentchanges</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-recentchangeslinked&amp;action=edit tooltip-recentchangeslinked]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-recentchangeslinked|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Recent changes in pages linking to this page &amp;#91;alt-c]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-recentchangeslinked</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-rss&amp;action=edit tooltip-rss]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-rss|Talk]]
+&lt;/td&gt;&lt;td&gt;
+RSS feed for this page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-rss</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-save&amp;action=edit tooltip-save]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-save|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Save your changes &amp;#91;alt-s]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-save</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-search&amp;action=edit tooltip-search]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-search|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Search this wiki &amp;#91;alt-f]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-search</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-sitesupport&amp;action=edit tooltip-sitesupport]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-sitesupport|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Support Wiktionary
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-sitesupport</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-specialpage&amp;action=edit tooltip-specialpage]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-specialpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This is a special page, you can&amp;#39;t edit the page itself.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-specialpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-specialpages&amp;action=edit tooltip-specialpages]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-specialpages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+List of all special pages &amp;#91;alt-q]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-specialpages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-talk&amp;action=edit tooltip-talk]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-talk|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Discussion about the content page &amp;#91;alt-t]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-talk</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-undelete&amp;action=edit tooltip-undelete]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-undelete|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Restore the $1 edits done to this page before it was deleted &amp;#91;alt-d]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-undelete</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-unwatch&amp;action=edit tooltip-unwatch]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-unwatch|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Remove this page from your watchlist &amp;#91;alt-w]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-unwatch</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-upload&amp;action=edit tooltip-upload]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-upload|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Upload images or media files &amp;#91;alt-u]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-upload</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-userpage&amp;action=edit tooltip-userpage]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-userpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+My user page &amp;#91;alt-.]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-userpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-viewsource&amp;action=edit tooltip-viewsource]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-viewsource|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This page is protected. You can view its source. &amp;#91;alt-e]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-viewsource</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-watch&amp;action=edit tooltip-watch]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-watch|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Add this page to your watchlist &amp;#91;alt-w]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-watch</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-watchlist&amp;action=edit tooltip-watchlist]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-watchlist|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The list of pages you&amp;#39;re monitoring for changes. &amp;#91;alt-l]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-watchlist</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-whatlinkshere&amp;action=edit tooltip-whatlinkshere]&lt;br&gt;
+[[MediaWiki_talk:Tooltip-whatlinkshere|Talk]]
+&lt;/td&gt;&lt;td&gt;
+List of all wiki pages that link here &amp;#91;alt-b]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Tooltip-whatlinkshere</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uclinks&amp;action=edit uclinks]&lt;br&gt;
+[[MediaWiki_talk:Uclinks|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View the last $1 changes; view the last $2 days.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uclinks</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ucnote&amp;action=edit ucnote]&lt;br&gt;
+[[MediaWiki_talk:Ucnote|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Below are this user&amp;#39;s last &amp;lt;b&amp;gt;$1&amp;lt;/b&amp;gt; changes in the last &amp;lt;b&amp;gt;$2&amp;lt;/b&amp;gt; days.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Ucnote</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uctop&amp;action=edit uctop]&lt;br&gt;
+[[MediaWiki_talk:Uctop|Talk]]
+&lt;/td&gt;&lt;td&gt;
+ (top)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uctop</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unblockip&amp;action=edit unblockip]&lt;br&gt;
+[[MediaWiki_talk:Unblockip|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Unblock user
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unblockip</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unblockiptext&amp;action=edit unblockiptext]&lt;br&gt;
+[[MediaWiki_talk:Unblockiptext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Use the form below to restore write access
+to a previously blocked IP address or username.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unblockiptext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unblocklink&amp;action=edit unblocklink]&lt;br&gt;
+[[MediaWiki_talk:Unblocklink|Talk]]
+&lt;/td&gt;&lt;td&gt;
+unblock
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unblocklink</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unblocklogentry&amp;action=edit unblocklogentry]&lt;br&gt;
+[[MediaWiki_talk:Unblocklogentry|Talk]]
+&lt;/td&gt;&lt;td&gt;
+unblocked &amp;quot;$1&amp;quot;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unblocklogentry</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undelete&amp;action=edit undelete]&lt;br&gt;
+[[MediaWiki_talk:Undelete|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Restore deleted page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Undelete</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undelete_short&amp;action=edit undelete_short]&lt;br&gt;
+[[MediaWiki_talk:Undelete_short|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Undelete $1 edits
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Undelete_short</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletearticle&amp;action=edit undeletearticle]&lt;br&gt;
+[[MediaWiki_talk:Undeletearticle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Restore deleted page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Undeletearticle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletebtn&amp;action=edit undeletebtn]&lt;br&gt;
+[[MediaWiki_talk:Undeletebtn|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Restore!
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Undeletebtn</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletedarticle&amp;action=edit undeletedarticle]&lt;br&gt;
+[[MediaWiki_talk:Undeletedarticle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+restored &amp;quot;$1&amp;quot;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Undeletedarticle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletedtext&amp;action=edit undeletedtext]&lt;br&gt;
+[[MediaWiki_talk:Undeletedtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;#91;&amp;#91;$1]] has been successfully restored.
+See &amp;#91;&amp;#91;Wiktionary:Deletion_log]] for a record of recent deletions and restorations.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Undeletedtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletehistory&amp;action=edit undeletehistory]&lt;br&gt;
+[[MediaWiki_talk:Undeletehistory|Talk]]
+&lt;/td&gt;&lt;td&gt;
+If you restore the page, all revisions will be restored to the history.
+If a new page with the same name has been created since the deletion, the restored
+revisions will appear in the prior history, and the current revision of the live page
+will not be automatically replaced.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Undeletehistory</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletepage&amp;action=edit undeletepage]&lt;br&gt;
+[[MediaWiki_talk:Undeletepage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View and restore deleted pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Undeletepage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletepagetext&amp;action=edit undeletepagetext]&lt;br&gt;
+[[MediaWiki_talk:Undeletepagetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The following pages have been deleted but are still in the archive and
+can be restored. The archive may be periodically cleaned out.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Undeletepagetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeleterevision&amp;action=edit undeleterevision]&lt;br&gt;
+[[MediaWiki_talk:Undeleterevision|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Deleted revision as of $1
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Undeleterevision</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeleterevisions&amp;action=edit undeleterevisions]&lt;br&gt;
+[[MediaWiki_talk:Undeleterevisions|Talk]]
+&lt;/td&gt;&lt;td&gt;
+$1 revisions archived
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Undeleterevisions</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unexpected&amp;action=edit unexpected]&lt;br&gt;
+[[MediaWiki_talk:Unexpected|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Unexpected value: &amp;quot;$1&amp;quot;=&amp;quot;$2&amp;quot;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unexpected</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unlockbtn&amp;action=edit unlockbtn]&lt;br&gt;
+[[MediaWiki_talk:Unlockbtn|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Unlock database
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unlockbtn</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unlockconfirm&amp;action=edit unlockconfirm]&lt;br&gt;
+[[MediaWiki_talk:Unlockconfirm|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Yes, I really want to unlock the database.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unlockconfirm</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unlockdb&amp;action=edit unlockdb]&lt;br&gt;
+[[MediaWiki_talk:Unlockdb|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Unlock database
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unlockdb</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unlockdbsuccesssub&amp;action=edit unlockdbsuccesssub]&lt;br&gt;
+[[MediaWiki_talk:Unlockdbsuccesssub|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Database lock removed
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unlockdbsuccesssub</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unlockdbsuccesstext&amp;action=edit unlockdbsuccesstext]&lt;br&gt;
+[[MediaWiki_talk:Unlockdbsuccesstext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The database has been unlocked.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unlockdbsuccesstext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unlockdbtext&amp;action=edit unlockdbtext]&lt;br&gt;
+[[MediaWiki_talk:Unlockdbtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Unlocking the database will restore the ability of all
+users to edit pages, change their preferences, edit their watchlists, and
+other things requiring changes in the database.
+Please confirm that this is what you intend to do.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unlockdbtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unprotect&amp;action=edit unprotect]&lt;br&gt;
+[[MediaWiki_talk:Unprotect|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Unprotect
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unprotect</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unprotectcomment&amp;action=edit unprotectcomment]&lt;br&gt;
+[[MediaWiki_talk:Unprotectcomment|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Reason for unprotecting
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unprotectcomment</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unprotectedarticle&amp;action=edit unprotectedarticle]&lt;br&gt;
+[[MediaWiki_talk:Unprotectedarticle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+unprotected &amp;#91;&amp;#91;$1]]
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unprotectedarticle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unprotectsub&amp;action=edit unprotectsub]&lt;br&gt;
+[[MediaWiki_talk:Unprotectsub|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(Unprotecting &amp;quot;$1&amp;quot;)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unprotectsub</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unprotectthispage&amp;action=edit unprotectthispage]&lt;br&gt;
+[[MediaWiki_talk:Unprotectthispage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Unprotect this page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unprotectthispage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unusedimages&amp;action=edit unusedimages]&lt;br&gt;
+[[MediaWiki_talk:Unusedimages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Unused images
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unusedimages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unusedimagestext&amp;action=edit unusedimagestext]&lt;br&gt;
+[[MediaWiki_talk:Unusedimagestext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;p&amp;gt;Please note that other web sites may link to an image with
+a direct URL, and so may still be listed here despite being
+in active use.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unusedimagestext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unwatch&amp;action=edit unwatch]&lt;br&gt;
+[[MediaWiki_talk:Unwatch|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Unwatch
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unwatch</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unwatchthispage&amp;action=edit unwatchthispage]&lt;br&gt;
+[[MediaWiki_talk:Unwatchthispage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Stop watching
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Unwatchthispage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Updated&amp;action=edit updated]&lt;br&gt;
+[[MediaWiki_talk:Updated|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(Updated)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Updated</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Upload&amp;action=edit upload]&lt;br&gt;
+[[MediaWiki_talk:Upload|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Upload file
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Upload</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadbtn&amp;action=edit uploadbtn]&lt;br&gt;
+[[MediaWiki_talk:Uploadbtn|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Upload file
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploadbtn</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploaddisabled&amp;action=edit uploaddisabled]&lt;br&gt;
+[[MediaWiki_talk:Uploaddisabled|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Sorry, uploading is disabled.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploaddisabled</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadedfiles&amp;action=edit uploadedfiles]&lt;br&gt;
+[[MediaWiki_talk:Uploadedfiles|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Uploaded files
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploadedfiles</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadedimage&amp;action=edit uploadedimage]&lt;br&gt;
+[[MediaWiki_talk:Uploadedimage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+uploaded &amp;quot;$1&amp;quot;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploadedimage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploaderror&amp;action=edit uploaderror]&lt;br&gt;
+[[MediaWiki_talk:Uploaderror|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Upload error
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploaderror</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadfile&amp;action=edit uploadfile]&lt;br&gt;
+[[MediaWiki_talk:Uploadfile|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Upload images, sounds, documents etc.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploadfile</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadlink&amp;action=edit uploadlink]&lt;br&gt;
+[[MediaWiki_talk:Uploadlink|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Upload images
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploadlink</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadlog&amp;action=edit uploadlog]&lt;br&gt;
+[[MediaWiki_talk:Uploadlog|Talk]]
+&lt;/td&gt;&lt;td&gt;
+upload log
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploadlog</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadlogpage&amp;action=edit uploadlogpage]&lt;br&gt;
+[[MediaWiki_talk:Uploadlogpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Upload_log
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploadlogpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadlogpagetext&amp;action=edit uploadlogpagetext]&lt;br&gt;
+[[MediaWiki_talk:Uploadlogpagetext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Below is a list of the most recent file uploads.
+All times shown are server time (UTC).
+&amp;lt;ul&amp;gt;
+&amp;lt;/ul&amp;gt;
+
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploadlogpagetext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadnologin&amp;action=edit uploadnologin]&lt;br&gt;
+[[MediaWiki_talk:Uploadnologin|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Not logged in
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploadnologin</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadnologintext&amp;action=edit uploadnologintext]&lt;br&gt;
+[[MediaWiki_talk:Uploadnologintext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You must be &amp;lt;a href=&amp;quot;/wiki/Special:Userlogin&amp;quot;&amp;gt;logged in&amp;lt;/a&amp;gt;
+to upload files.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploadnologintext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadtext&amp;action=edit uploadtext]&lt;br&gt;
+[[MediaWiki_talk:Uploadtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;strong&amp;gt;STOP!&amp;lt;/strong&amp;gt; Before you upload here,
+make sure to read and follow the &amp;lt;a href=&amp;quot;/wiki/Special:Image_use_policy&amp;quot;&amp;gt;image use policy&amp;lt;/a&amp;gt;.
+&amp;lt;p&amp;gt;If a file with the name you are specifying already
+exists on the wiki, it&amp;#39;ll be replaced without warning.
+So unless you mean to update a file, it&amp;#39;s a good idea
+to first check if such a file exists.
+&amp;lt;p&amp;gt;To view or search previously uploaded images,
+go to the &amp;lt;a href=&amp;quot;/wiki/Special:Imagelist&amp;quot;&amp;gt;list of uploaded images&amp;lt;/a&amp;gt;.
+Uploads and deletions are logged on the &amp;lt;a href=&amp;quot;/wiki/Wiktionary:Upload_log&amp;quot;&amp;gt;upload log&amp;lt;/a&amp;gt;.
+&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Use the form below to upload new image files for use in
+illustrating your pages.
+On most browsers, you will see a &amp;quot;Browse...&amp;quot; button, which will
+bring up your operating system&amp;#39;s standard file open dialog.
+Choosing a file will fill the name of that file into the text
+field next to the button.
+You must also check the box affirming that you are not
+violating any copyrights by uploading the file.
+Press the &amp;quot;Upload&amp;quot; button to finish the upload.
+This may take some time if you have a slow internet connection.
+&amp;lt;p&amp;gt;The preferred formats are JPEG for photographic images, PNG
+for drawings and other iconic images, and OGG for sounds.
+Please name your files descriptively to avoid confusion.
+To include the image in a page, use a link in the form
+&amp;lt;b&amp;gt;&amp;#91;&amp;#91;Image:file.jpg]]&amp;lt;/b&amp;gt; or &amp;lt;b&amp;gt;&amp;#91;&amp;#91;Image:file.png&amp;#124;alt text]]&amp;lt;/b&amp;gt;
+or &amp;lt;b&amp;gt;&amp;#91;&amp;#91;Media:file.ogg]]&amp;lt;/b&amp;gt; for sounds.
+&amp;lt;p&amp;gt;Please note that as with wiki pages, others may edit or
+delete your uploads if they think it serves the project, and
+you may be blocked from uploading if you abuse the system.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploadtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadwarning&amp;action=edit uploadwarning]&lt;br&gt;
+[[MediaWiki_talk:Uploadwarning|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Upload warning
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Uploadwarning</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:User_rights_set&amp;action=edit user_rights_set]&lt;br&gt;
+[[MediaWiki_talk:User_rights_set|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;b&amp;gt;User rights for &amp;quot;$1&amp;quot; updated&amp;lt;/b&amp;gt;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:User_rights_set</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Usercssjs&amp;action=edit usercssjs]&lt;br&gt;
+[[MediaWiki_talk:Usercssjs|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;#39;&amp;#39;&amp;#39;Note:&amp;#39;&amp;#39;&amp;#39; After saving, you have to tell your bowser to get the new version: &amp;#39;&amp;#39;&amp;#39;Mozilla:&amp;#39;&amp;#39;&amp;#39; click &amp;#39;&amp;#39;reload&amp;#39;&amp;#39;(or &amp;#39;&amp;#39;ctrl-r&amp;#39;&amp;#39;), &amp;#39;&amp;#39;&amp;#39;IE / Opera:&amp;#39;&amp;#39;&amp;#39; &amp;#39;&amp;#39;ctrl-f5&amp;#39;&amp;#39;, &amp;#39;&amp;#39;&amp;#39;Safari:&amp;#39;&amp;#39;&amp;#39; &amp;#39;&amp;#39;cmd-r&amp;#39;&amp;#39;, &amp;#39;&amp;#39;&amp;#39;Konqueror&amp;#39;&amp;#39;&amp;#39; &amp;#39;&amp;#39;ctrl-r&amp;#39;&amp;#39;.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Usercssjs</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Usercssjsyoucanpreview&amp;action=edit usercssjsyoucanpreview]&lt;br&gt;
+[[MediaWiki_talk:Usercssjsyoucanpreview|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;strong&amp;gt;Tip:&amp;lt;/strong&amp;gt; Use the &amp;#39;Show preview&amp;#39; button to test your new css/js before saving.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Usercssjsyoucanpreview</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Usercsspreview&amp;action=edit usercsspreview]&lt;br&gt;
+[[MediaWiki_talk:Usercsspreview|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;#39;&amp;#39;&amp;#39;Remember that you are only previewing your user css, it has not yet been saved!&amp;#39;&amp;#39;&amp;#39;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Usercsspreview</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userexists&amp;action=edit userexists]&lt;br&gt;
+[[MediaWiki_talk:Userexists|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The user name you entered is already in use. Please choose a different name.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Userexists</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userjspreview&amp;action=edit userjspreview]&lt;br&gt;
+[[MediaWiki_talk:Userjspreview|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;#39;&amp;#39;&amp;#39;Remember that you are only testing/previewing your user javascript, it has not yet been saved!&amp;#39;&amp;#39;&amp;#39;
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Userjspreview</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userlogin&amp;action=edit userlogin]&lt;br&gt;
+[[MediaWiki_talk:Userlogin|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Log in
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Userlogin</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userlogout&amp;action=edit userlogout]&lt;br&gt;
+[[MediaWiki_talk:Userlogout|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Log out
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Userlogout</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Usermailererror&amp;action=edit usermailererror]&lt;br&gt;
+[[MediaWiki_talk:Usermailererror|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Mail object returned error:
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Usermailererror</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userpage&amp;action=edit userpage]&lt;br&gt;
+[[MediaWiki_talk:Userpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View user page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Userpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userstats&amp;action=edit userstats]&lt;br&gt;
+[[MediaWiki_talk:Userstats|Talk]]
+&lt;/td&gt;&lt;td&gt;
+User statistics
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Userstats</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userstatstext&amp;action=edit userstatstext]&lt;br&gt;
+[[MediaWiki_talk:Userstatstext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+There are &amp;#39;&amp;#39;&amp;#39;$1&amp;#39;&amp;#39;&amp;#39; registered users.
+&amp;#39;&amp;#39;&amp;#39;$2&amp;#39;&amp;#39;&amp;#39; of these are administrators (see $3).
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Userstatstext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Version&amp;action=edit version]&lt;br&gt;
+[[MediaWiki_talk:Version|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Version
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Version</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Viewcount&amp;action=edit viewcount]&lt;br&gt;
+[[MediaWiki_talk:Viewcount|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This page has been accessed $1 times.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Viewcount</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Viewprevnext&amp;action=edit viewprevnext]&lt;br&gt;
+[[MediaWiki_talk:Viewprevnext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View ($1) ($2) ($3).
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Viewprevnext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Viewsource&amp;action=edit viewsource]&lt;br&gt;
+[[MediaWiki_talk:Viewsource|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View source
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Viewsource</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Viewtalkpage&amp;action=edit viewtalkpage]&lt;br&gt;
+[[MediaWiki_talk:Viewtalkpage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View discussion
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Viewtalkpage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wantedpages&amp;action=edit wantedpages]&lt;br&gt;
+[[MediaWiki_talk:Wantedpages|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wanted pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Wantedpages</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watch&amp;action=edit watch]&lt;br&gt;
+[[MediaWiki_talk:Watch|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Watch
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watch</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchdetails&amp;action=edit watchdetails]&lt;br&gt;
+[[MediaWiki_talk:Watchdetails|Talk]]
+&lt;/td&gt;&lt;td&gt;
+($1 pages watched not counting talk pages;
+$2 total pages edited since cutoff;
+$3...
+&amp;lt;a href=&amp;#39;$4&amp;#39;&amp;gt;show and edit complete list&amp;lt;/a&amp;gt;.)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watchdetails</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watcheditlist&amp;action=edit watcheditlist]&lt;br&gt;
+[[MediaWiki_talk:Watcheditlist|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Here&amp;#39;s an alphabetical list of your
+watched pages. Check the boxes of pages you want to remove
+from your watchlist and click the &amp;#39;remove checked&amp;#39; button
+at the bottom of the screen.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watcheditlist</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchlist&amp;action=edit watchlist]&lt;br&gt;
+[[MediaWiki_talk:Watchlist|Talk]]
+&lt;/td&gt;&lt;td&gt;
+My watchlist
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watchlist</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchlistcontains&amp;action=edit watchlistcontains]&lt;br&gt;
+[[MediaWiki_talk:Watchlistcontains|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Your watchlist contains $1 pages.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watchlistcontains</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchlistsub&amp;action=edit watchlistsub]&lt;br&gt;
+[[MediaWiki_talk:Watchlistsub|Talk]]
+&lt;/td&gt;&lt;td&gt;
+(for user &amp;quot;$1&amp;quot;)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watchlistsub</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchmethod-list&amp;action=edit watchmethod-list]&lt;br&gt;
+[[MediaWiki_talk:Watchmethod-list|Talk]]
+&lt;/td&gt;&lt;td&gt;
+checking watched pages for recent edits
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watchmethod-list</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchmethod-recent&amp;action=edit watchmethod-recent]&lt;br&gt;
+[[MediaWiki_talk:Watchmethod-recent|Talk]]
+&lt;/td&gt;&lt;td&gt;
+checking recent edits for watched pages
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watchmethod-recent</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchnochange&amp;action=edit watchnochange]&lt;br&gt;
+[[MediaWiki_talk:Watchnochange|Talk]]
+&lt;/td&gt;&lt;td&gt;
+None of your watched items were edited in the time period displayed.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watchnochange</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchnologin&amp;action=edit watchnologin]&lt;br&gt;
+[[MediaWiki_talk:Watchnologin|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Not logged in
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watchnologin</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchnologintext&amp;action=edit watchnologintext]&lt;br&gt;
+[[MediaWiki_talk:Watchnologintext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You must be &amp;lt;a href=&amp;quot;/wiki/Special:Userlogin&amp;quot;&amp;gt;logged in&amp;lt;/a&amp;gt;
+to modify your watchlist.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watchnologintext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchthis&amp;action=edit watchthis]&lt;br&gt;
+[[MediaWiki_talk:Watchthis|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Watch this page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watchthis</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchthispage&amp;action=edit watchthispage]&lt;br&gt;
+[[MediaWiki_talk:Watchthispage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Watch this page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Watchthispage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Welcomecreation&amp;action=edit welcomecreation]&lt;br&gt;
+[[MediaWiki_talk:Welcomecreation|Talk]]
+&lt;/td&gt;&lt;td&gt;
+&amp;lt;h2&amp;gt;Welcome, $1!&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Your account has been created.
+Don&amp;#39;t forget to change your Wiktionary preferences.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Welcomecreation</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whatlinkshere&amp;action=edit whatlinkshere]&lt;br&gt;
+[[MediaWiki_talk:Whatlinkshere|Talk]]
+&lt;/td&gt;&lt;td&gt;
+What links here
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Whatlinkshere</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whitelistacctext&amp;action=edit whitelistacctext]&lt;br&gt;
+[[MediaWiki_talk:Whitelistacctext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+To be allowed to create accounts in this Wiki you have to &amp;#91;&amp;#91;Special:Userlogin&amp;#124;log]] in and have the appropriate permissions.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Whitelistacctext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whitelistacctitle&amp;action=edit whitelistacctitle]&lt;br&gt;
+[[MediaWiki_talk:Whitelistacctitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You are not allowed to create an account
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Whitelistacctitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whitelistedittext&amp;action=edit whitelistedittext]&lt;br&gt;
+[[MediaWiki_talk:Whitelistedittext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You have to &amp;#91;&amp;#91;Special:Userlogin&amp;#124;login]] to edit pages.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Whitelistedittext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whitelistedittitle&amp;action=edit whitelistedittitle]&lt;br&gt;
+[[MediaWiki_talk:Whitelistedittitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Login required to edit
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Whitelistedittitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whitelistreadtext&amp;action=edit whitelistreadtext]&lt;br&gt;
+[[MediaWiki_talk:Whitelistreadtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+You have to &amp;#91;&amp;#91;Special:Userlogin&amp;#124;login]] to read pages.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Whitelistreadtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whitelistreadtitle&amp;action=edit whitelistreadtitle]&lt;br&gt;
+[[MediaWiki_talk:Whitelistreadtitle|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Login required to read
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Whitelistreadtitle</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wikipediapage&amp;action=edit wikipediapage]&lt;br&gt;
+[[MediaWiki_talk:Wikipediapage|Talk]]
+&lt;/td&gt;&lt;td&gt;
+View project page
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Wikipediapage</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wikititlesuffix&amp;action=edit wikititlesuffix]&lt;br&gt;
+[[MediaWiki_talk:Wikititlesuffix|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Wiktionary
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Wikititlesuffix</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wlnote&amp;action=edit wlnote]&lt;br&gt;
+[[MediaWiki_talk:Wlnote|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Below are the last $1 changes in the last &amp;lt;b&amp;gt;$2&amp;lt;/b&amp;gt; hours.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Wlnote</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wlsaved&amp;action=edit wlsaved]&lt;br&gt;
+[[MediaWiki_talk:Wlsaved|Talk]]
+&lt;/td&gt;&lt;td&gt;
+This is a saved version of your watchlist.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Wlsaved</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wlshowlast&amp;action=edit wlshowlast]&lt;br&gt;
+[[MediaWiki_talk:Wlshowlast|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Show last $1 hours $2 days $3
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Wlshowlast</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wrong_wfQuery_params&amp;action=edit wrong_wfQuery_params]&lt;br&gt;
+[[MediaWiki_talk:Wrong_wfQuery_params|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Incorrect parameters to wfQuery()&amp;lt;br /&amp;gt;
+Function: $1&amp;lt;br /&amp;gt;
+Query: $2
+
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Wrong_wfQuery_params</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wrongpassword&amp;action=edit wrongpassword]&lt;br&gt;
+[[MediaWiki_talk:Wrongpassword|Talk]]
+&lt;/td&gt;&lt;td&gt;
+The password you entered is incorrect. Please try again.
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Wrongpassword</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yourdiff&amp;action=edit yourdiff]&lt;br&gt;
+[[MediaWiki_talk:Yourdiff|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Differences
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Yourdiff</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Youremail&amp;action=edit youremail]&lt;br&gt;
+[[MediaWiki_talk:Youremail|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Your email*
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Youremail</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yourname&amp;action=edit yourname]&lt;br&gt;
+[[MediaWiki_talk:Yourname|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Your user name
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Yourname</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yournick&amp;action=edit yournick]&lt;br&gt;
+[[MediaWiki_talk:Yournick|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Your nickname (for signatures)
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Yournick</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yourpassword&amp;action=edit yourpassword]&lt;br&gt;
+[[MediaWiki_talk:Yourpassword|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Your password
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Yourpassword</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yourpasswordagain&amp;action=edit yourpasswordagain]&lt;br&gt;
+[[MediaWiki_talk:Yourpasswordagain|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Retype password
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Yourpasswordagain</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yourrealname&amp;action=edit yourrealname]&lt;br&gt;
+[[MediaWiki_talk:Yourrealname|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Your real name*
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Yourrealname</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yourtext&amp;action=edit yourtext]&lt;br&gt;
+[[MediaWiki_talk:Yourtext|Talk]]
+&lt;/td&gt;&lt;td&gt;
+Your text
+&lt;/td&gt;&lt;td&gt;
+<template lineStart="1"><title>int:Yourtext</title></template>
+&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
+
+</root> \ No newline at end of file
diff --git a/tests/parser/preprocess/All_system_messages.txt b/tests/parser/preprocess/All_system_messages.txt
new file mode 100644
index 00000000..fc10d7cf
--- /dev/null
+++ b/tests/parser/preprocess/All_system_messages.txt
@@ -0,0 +1,5645 @@
+{{int:allmessagestext}}
+
+<table border=1 width=100%><tr><td>
+'''Name'''
+</td><td>
+'''Default text'''
+</td><td>
+'''Current text'''
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:1movedto2&action=edit 1movedto2]<br>
+[[MediaWiki_talk:1movedto2|Talk]]
+</td><td>
+$1 moved to $2
+</td><td>
+{{int:1movedto2}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Monobook.css&action=edit Monobook.css]<br>
+[[MediaWiki_talk:Monobook.css|Talk]]
+</td><td>
+/* edit this file to customize the monobook skin for the entire site */
+</td><td>
+{{int:Monobook.css}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:About&action=edit about]<br>
+[[MediaWiki_talk:About|Talk]]
+</td><td>
+About
+</td><td>
+{{int:About}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Aboutpage&action=edit aboutpage]<br>
+[[MediaWiki_talk:Aboutpage|Talk]]
+</td><td>
+Wiktionary:About
+</td><td>
+{{int:Aboutpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Aboutwikipedia&action=edit aboutwikipedia]<br>
+[[MediaWiki_talk:Aboutwikipedia|Talk]]
+</td><td>
+About Wiktionary
+</td><td>
+{{int:Aboutwikipedia}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-addsection&action=edit accesskey-addsection]<br>
+[[MediaWiki_talk:Accesskey-addsection|Talk]]
+</td><td>
++
+</td><td>
+{{int:Accesskey-addsection}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-anontalk&action=edit accesskey-anontalk]<br>
+[[MediaWiki_talk:Accesskey-anontalk|Talk]]
+</td><td>
+n
+</td><td>
+{{int:Accesskey-anontalk}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-anonuserpage&action=edit accesskey-anonuserpage]<br>
+[[MediaWiki_talk:Accesskey-anonuserpage|Talk]]
+</td><td>
+.
+</td><td>
+{{int:Accesskey-anonuserpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-article&action=edit accesskey-article]<br>
+[[MediaWiki_talk:Accesskey-article|Talk]]
+</td><td>
+a
+</td><td>
+{{int:Accesskey-article}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-compareselectedversions&action=edit accesskey-compareselectedversions]<br>
+[[MediaWiki_talk:Accesskey-compareselectedversions|Talk]]
+</td><td>
+v
+</td><td>
+{{int:Accesskey-compareselectedversions}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-contributions&action=edit accesskey-contributions]<br>
+[[MediaWiki_talk:Accesskey-contributions|Talk]]
+</td><td>
+&amp;lt;accesskey-contributions&amp;gt;
+</td><td>
+{{int:Accesskey-contributions}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-currentevents&action=edit accesskey-currentevents]<br>
+[[MediaWiki_talk:Accesskey-currentevents|Talk]]
+</td><td>
+&amp;lt;accesskey-currentevents&amp;gt;
+</td><td>
+{{int:Accesskey-currentevents}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-delete&action=edit accesskey-delete]<br>
+[[MediaWiki_talk:Accesskey-delete|Talk]]
+</td><td>
+d
+</td><td>
+{{int:Accesskey-delete}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-edit&action=edit accesskey-edit]<br>
+[[MediaWiki_talk:Accesskey-edit|Talk]]
+</td><td>
+e
+</td><td>
+{{int:Accesskey-edit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-emailuser&action=edit accesskey-emailuser]<br>
+[[MediaWiki_talk:Accesskey-emailuser|Talk]]
+</td><td>
+&amp;lt;accesskey-emailuser&amp;gt;
+</td><td>
+{{int:Accesskey-emailuser}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-help&action=edit accesskey-help]<br>
+[[MediaWiki_talk:Accesskey-help|Talk]]
+</td><td>
+&amp;lt;accesskey-help&amp;gt;
+</td><td>
+{{int:Accesskey-help}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-history&action=edit accesskey-history]<br>
+[[MediaWiki_talk:Accesskey-history|Talk]]
+</td><td>
+h
+</td><td>
+{{int:Accesskey-history}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-login&action=edit accesskey-login]<br>
+[[MediaWiki_talk:Accesskey-login|Talk]]
+</td><td>
+o
+</td><td>
+{{int:Accesskey-login}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-logout&action=edit accesskey-logout]<br>
+[[MediaWiki_talk:Accesskey-logout|Talk]]
+</td><td>
+o
+</td><td>
+{{int:Accesskey-logout}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-mainpage&action=edit accesskey-mainpage]<br>
+[[MediaWiki_talk:Accesskey-mainpage|Talk]]
+</td><td>
+z
+</td><td>
+{{int:Accesskey-mainpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-minoredit&action=edit accesskey-minoredit]<br>
+[[MediaWiki_talk:Accesskey-minoredit|Talk]]
+</td><td>
+i
+</td><td>
+{{int:Accesskey-minoredit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-move&action=edit accesskey-move]<br>
+[[MediaWiki_talk:Accesskey-move|Talk]]
+</td><td>
+m
+</td><td>
+{{int:Accesskey-move}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-mycontris&action=edit accesskey-mycontris]<br>
+[[MediaWiki_talk:Accesskey-mycontris|Talk]]
+</td><td>
+y
+</td><td>
+{{int:Accesskey-mycontris}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-mytalk&action=edit accesskey-mytalk]<br>
+[[MediaWiki_talk:Accesskey-mytalk|Talk]]
+</td><td>
+n
+</td><td>
+{{int:Accesskey-mytalk}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-portal&action=edit accesskey-portal]<br>
+[[MediaWiki_talk:Accesskey-portal|Talk]]
+</td><td>
+&amp;lt;accesskey-portal&amp;gt;
+</td><td>
+{{int:Accesskey-portal}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-preferences&action=edit accesskey-preferences]<br>
+[[MediaWiki_talk:Accesskey-preferences|Talk]]
+</td><td>
+&amp;lt;accesskey-preferences&amp;gt;
+</td><td>
+{{int:Accesskey-preferences}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-preview&action=edit accesskey-preview]<br>
+[[MediaWiki_talk:Accesskey-preview|Talk]]
+</td><td>
+p
+</td><td>
+{{int:Accesskey-preview}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-protect&action=edit accesskey-protect]<br>
+[[MediaWiki_talk:Accesskey-protect|Talk]]
+</td><td>
+=
+</td><td>
+{{int:Accesskey-protect}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-randompage&action=edit accesskey-randompage]<br>
+[[MediaWiki_talk:Accesskey-randompage|Talk]]
+</td><td>
+x
+</td><td>
+{{int:Accesskey-randompage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-recentchanges&action=edit accesskey-recentchanges]<br>
+[[MediaWiki_talk:Accesskey-recentchanges|Talk]]
+</td><td>
+r
+</td><td>
+{{int:Accesskey-recentchanges}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-recentchangeslinked&action=edit accesskey-recentchangeslinked]<br>
+[[MediaWiki_talk:Accesskey-recentchangeslinked|Talk]]
+</td><td>
+c
+</td><td>
+{{int:Accesskey-recentchangeslinked}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-save&action=edit accesskey-save]<br>
+[[MediaWiki_talk:Accesskey-save|Talk]]
+</td><td>
+s
+</td><td>
+{{int:Accesskey-save}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-search&action=edit accesskey-search]<br>
+[[MediaWiki_talk:Accesskey-search|Talk]]
+</td><td>
+f
+</td><td>
+{{int:Accesskey-search}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-sitesupport&action=edit accesskey-sitesupport]<br>
+[[MediaWiki_talk:Accesskey-sitesupport|Talk]]
+</td><td>
+&amp;lt;accesskey-sitesupport&amp;gt;
+</td><td>
+{{int:Accesskey-sitesupport}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-specialpage&action=edit accesskey-specialpage]<br>
+[[MediaWiki_talk:Accesskey-specialpage|Talk]]
+</td><td>
+&amp;lt;accesskey-specialpage&amp;gt;
+</td><td>
+{{int:Accesskey-specialpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-specialpages&action=edit accesskey-specialpages]<br>
+[[MediaWiki_talk:Accesskey-specialpages|Talk]]
+</td><td>
+q
+</td><td>
+{{int:Accesskey-specialpages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-talk&action=edit accesskey-talk]<br>
+[[MediaWiki_talk:Accesskey-talk|Talk]]
+</td><td>
+t
+</td><td>
+{{int:Accesskey-talk}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-undelete&action=edit accesskey-undelete]<br>
+[[MediaWiki_talk:Accesskey-undelete|Talk]]
+</td><td>
+d
+</td><td>
+{{int:Accesskey-undelete}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-unwatch&action=edit accesskey-unwatch]<br>
+[[MediaWiki_talk:Accesskey-unwatch|Talk]]
+</td><td>
+w
+</td><td>
+{{int:Accesskey-unwatch}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-upload&action=edit accesskey-upload]<br>
+[[MediaWiki_talk:Accesskey-upload|Talk]]
+</td><td>
+u
+</td><td>
+{{int:Accesskey-upload}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-userpage&action=edit accesskey-userpage]<br>
+[[MediaWiki_talk:Accesskey-userpage|Talk]]
+</td><td>
+.
+</td><td>
+{{int:Accesskey-userpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-viewsource&action=edit accesskey-viewsource]<br>
+[[MediaWiki_talk:Accesskey-viewsource|Talk]]
+</td><td>
+e
+</td><td>
+{{int:Accesskey-viewsource}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-watch&action=edit accesskey-watch]<br>
+[[MediaWiki_talk:Accesskey-watch|Talk]]
+</td><td>
+w
+</td><td>
+{{int:Accesskey-watch}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-watchlist&action=edit accesskey-watchlist]<br>
+[[MediaWiki_talk:Accesskey-watchlist|Talk]]
+</td><td>
+l
+</td><td>
+{{int:Accesskey-watchlist}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accesskey-whatlinkshere&action=edit accesskey-whatlinkshere]<br>
+[[MediaWiki_talk:Accesskey-whatlinkshere|Talk]]
+</td><td>
+b
+</td><td>
+{{int:Accesskey-whatlinkshere}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accmailtext&action=edit accmailtext]<br>
+[[MediaWiki_talk:Accmailtext|Talk]]
+</td><td>
+The Password for &#39;$1&#39; has been sent to $2.
+</td><td>
+{{int:Accmailtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Accmailtitle&action=edit accmailtitle]<br>
+[[MediaWiki_talk:Accmailtitle|Talk]]
+</td><td>
+Password sent.
+</td><td>
+{{int:Accmailtitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Actioncomplete&action=edit actioncomplete]<br>
+[[MediaWiki_talk:Actioncomplete|Talk]]
+</td><td>
+Action complete
+</td><td>
+{{int:Actioncomplete}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Addedwatch&action=edit addedwatch]<br>
+[[MediaWiki_talk:Addedwatch|Talk]]
+</td><td>
+Added to watchlist
+</td><td>
+{{int:Addedwatch}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Addedwatchtext&action=edit addedwatchtext]<br>
+[[MediaWiki_talk:Addedwatchtext|Talk]]
+</td><td>
+The page &quot;$1&quot; has been added to your &#91;&#91;Special:Watchlist&#124;watchlist]].
+Future changes to this page and its associated Talk page will be listed there,
+and the page will appear &#39;&#39;&#39;bolded&#39;&#39;&#39; in the &#91;&#91;Special:Recentchanges&#124;list of recent changes]] to
+make it easier to pick out.
+
+&lt;p&gt;If you want to remove the page from your watchlist later, click &quot;Stop watching&quot; in the sidebar.
+</td><td>
+{{int:Addedwatchtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Addsection&action=edit addsection]<br>
+[[MediaWiki_talk:Addsection|Talk]]
+</td><td>
++
+</td><td>
+{{int:Addsection}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Administrators&action=edit administrators]<br>
+[[MediaWiki_talk:Administrators|Talk]]
+</td><td>
+Wiktionary:Administrators
+</td><td>
+{{int:Administrators}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Affirmation&action=edit affirmation]<br>
+[[MediaWiki_talk:Affirmation|Talk]]
+</td><td>
+I affirm that the copyright holder of this file
+agrees to license it under the terms of the $1.
+</td><td>
+{{int:Affirmation}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:All&action=edit all]<br>
+[[MediaWiki_talk:All|Talk]]
+</td><td>
+all
+</td><td>
+{{int:All}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Allmessages&action=edit allmessages]<br>
+[[MediaWiki_talk:Allmessages|Talk]]
+</td><td>
+All system messages
+</td><td>
+{{int:Allmessages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Allmessagestext&action=edit allmessagestext]<br>
+[[MediaWiki_talk:Allmessagestext|Talk]]
+</td><td>
+This is a list of all system messages available in the MediaWiki: namespace.
+</td><td>
+{{int:Allmessagestext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Allpages&action=edit allpages]<br>
+[[MediaWiki_talk:Allpages|Talk]]
+</td><td>
+All pages
+</td><td>
+{{int:Allpages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Alphaindexline&action=edit alphaindexline]<br>
+[[MediaWiki_talk:Alphaindexline|Talk]]
+</td><td>
+$1 to $2
+</td><td>
+{{int:Alphaindexline}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Alreadyloggedin&action=edit alreadyloggedin]<br>
+[[MediaWiki_talk:Alreadyloggedin|Talk]]
+</td><td>
+&lt;font color=red&gt;&lt;b&gt;User $1, you are already logged in!&lt;/b&gt;&lt;/font&gt;&lt;br /&gt;
+
+</td><td>
+{{int:Alreadyloggedin}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Alreadyrolled&action=edit alreadyrolled]<br>
+[[MediaWiki_talk:Alreadyrolled|Talk]]
+</td><td>
+Cannot rollback last edit of &#91;&#91;$1]]
+by &#91;&#91;User:$2&#124;$2]] (&#91;&#91;User talk:$2&#124;Talk]]); someone else has edited or rolled back the page already.
+
+Last edit was by &#91;&#91;User:$3&#124;$3]] (&#91;&#91;User talk:$3&#124;Talk]]).
+</td><td>
+{{int:Alreadyrolled}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ancientpages&action=edit ancientpages]<br>
+[[MediaWiki_talk:Ancientpages|Talk]]
+</td><td>
+Oldest pages
+</td><td>
+{{int:Ancientpages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:And&action=edit and]<br>
+[[MediaWiki_talk:And|Talk]]
+</td><td>
+and
+</td><td>
+{{int:And}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Anontalk&action=edit anontalk]<br>
+[[MediaWiki_talk:Anontalk|Talk]]
+</td><td>
+Talk for this IP
+</td><td>
+{{int:Anontalk}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Anontalkpagetext&action=edit anontalkpagetext]<br>
+[[MediaWiki_talk:Anontalkpagetext|Talk]]
+</td><td>
+----&#39;&#39;This is the discussion page for an anonymous user who has not created an account yet or who does not use it. We therefore have to use the numerical &#91;&#91;IP address]] to identify him/her. Such an IP address can be shared by several users. If you are an anonymous user and feel that irrelevant comments have been directed at you, please &#91;&#91;Special:Userlogin&#124;create an account or log in]] to avoid future confusion with other anonymous users.&#39;&#39;
+</td><td>
+{{int:Anontalkpagetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Anonymous&action=edit anonymous]<br>
+[[MediaWiki_talk:Anonymous|Talk]]
+</td><td>
+Anonymous user(s) of Wiktionary
+</td><td>
+{{int:Anonymous}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Article&action=edit article]<br>
+[[MediaWiki_talk:Article|Talk]]
+</td><td>
+Content page
+</td><td>
+{{int:Article}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Articleexists&action=edit articleexists]<br>
+[[MediaWiki_talk:Articleexists|Talk]]
+</td><td>
+A page of that name already exists, or the
+name you have chosen is not valid.
+Please choose another name.
+</td><td>
+{{int:Articleexists}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Articlepage&action=edit articlepage]<br>
+[[MediaWiki_talk:Articlepage|Talk]]
+</td><td>
+View content page
+</td><td>
+{{int:Articlepage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Asksql&action=edit asksql]<br>
+[[MediaWiki_talk:Asksql|Talk]]
+</td><td>
+SQL query
+</td><td>
+{{int:Asksql}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Asksqltext&action=edit asksqltext]<br>
+[[MediaWiki_talk:Asksqltext|Talk]]
+</td><td>
+Use the form below to make a direct query of the
+database.
+Use single quotes (&#39;like this&#39;) to delimit string literals.
+This can often add considerable load to the server, so please use
+this function sparingly.
+</td><td>
+{{int:Asksqltext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Autoblocker&action=edit autoblocker]<br>
+[[MediaWiki_talk:Autoblocker|Talk]]
+</td><td>
+Autoblocked because you share an IP address with &quot;$1&quot;. Reason &quot;$2&quot;.
+</td><td>
+{{int:Autoblocker}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badarticleerror&action=edit badarticleerror]<br>
+[[MediaWiki_talk:Badarticleerror|Talk]]
+</td><td>
+This action cannot be performed on this page.
+</td><td>
+{{int:Badarticleerror}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badfilename&action=edit badfilename]<br>
+[[MediaWiki_talk:Badfilename|Talk]]
+</td><td>
+Image name has been changed to &quot;$1&quot;.
+</td><td>
+{{int:Badfilename}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badfiletype&action=edit badfiletype]<br>
+[[MediaWiki_talk:Badfiletype|Talk]]
+</td><td>
+&quot;.$1&quot; is not a recommended image file format.
+</td><td>
+{{int:Badfiletype}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badipaddress&action=edit badipaddress]<br>
+[[MediaWiki_talk:Badipaddress|Talk]]
+</td><td>
+Invalid IP address
+</td><td>
+{{int:Badipaddress}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badquery&action=edit badquery]<br>
+[[MediaWiki_talk:Badquery|Talk]]
+</td><td>
+Badly formed search query
+</td><td>
+{{int:Badquery}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badquerytext&action=edit badquerytext]<br>
+[[MediaWiki_talk:Badquerytext|Talk]]
+</td><td>
+We could not process your query.
+This is probably because you have attempted to search for a
+word fewer than three letters long, which is not yet supported.
+It could also be that you have mistyped the expression, for
+example &quot;fish and and scales&quot;.
+Please try another query.
+</td><td>
+{{int:Badquerytext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badretype&action=edit badretype]<br>
+[[MediaWiki_talk:Badretype|Talk]]
+</td><td>
+The passwords you entered do not match.
+</td><td>
+{{int:Badretype}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badtitle&action=edit badtitle]<br>
+[[MediaWiki_talk:Badtitle|Talk]]
+</td><td>
+Bad title
+</td><td>
+{{int:Badtitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Badtitletext&action=edit badtitletext]<br>
+[[MediaWiki_talk:Badtitletext|Talk]]
+</td><td>
+The requested page title was invalid, empty, or
+an incorrectly linked inter-language or inter-wiki title.
+</td><td>
+{{int:Badtitletext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blanknamespace&action=edit blanknamespace]<br>
+[[MediaWiki_talk:Blanknamespace|Talk]]
+</td><td>
+(Main)
+</td><td>
+{{int:Blanknamespace}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blockedtext&action=edit blockedtext]<br>
+[[MediaWiki_talk:Blockedtext|Talk]]
+</td><td>
+Your user name or IP address has been blocked by $1.
+The reason given is this:&lt;br /&gt;&#39;&#39;$2&#39;&#39;&lt;p&gt;You may contact $1 or one of the other
+&#91;&#91;Wiktionary:Administrators&#124;administrators]] to discuss the block.
+
+Note that you may not use the &quot;email this user&quot; feature unless you have a valid email address registered in your &#91;&#91;Special:Preferences&#124;user preferences]].
+
+Your IP address is $3. Please include this address in any queries you make.
+
+</td><td>
+{{int:Blockedtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blockedtitle&action=edit blockedtitle]<br>
+[[MediaWiki_talk:Blockedtitle|Talk]]
+</td><td>
+User is blocked
+</td><td>
+{{int:Blockedtitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blockip&action=edit blockip]<br>
+[[MediaWiki_talk:Blockip|Talk]]
+</td><td>
+Block user
+</td><td>
+{{int:Blockip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blockipsuccesssub&action=edit blockipsuccesssub]<br>
+[[MediaWiki_talk:Blockipsuccesssub|Talk]]
+</td><td>
+Block succeeded
+</td><td>
+{{int:Blockipsuccesssub}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blockipsuccesstext&action=edit blockipsuccesstext]<br>
+[[MediaWiki_talk:Blockipsuccesstext|Talk]]
+</td><td>
+&quot;$1&quot; has been blocked.
+&lt;br /&gt;See &#91;&#91;Special:Ipblocklist&#124;IP block list]] to review blocks.
+</td><td>
+{{int:Blockipsuccesstext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blockiptext&action=edit blockiptext]<br>
+[[MediaWiki_talk:Blockiptext|Talk]]
+</td><td>
+Use the form below to block write access
+from a specific IP address or username.
+This should be done only only to prevent vandalism, and in
+accordance with &#91;&#91;Wiktionary:Policy&#124;policy]].
+Fill in a specific reason below (for example, citing particular
+pages that were vandalized).
+</td><td>
+{{int:Blockiptext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blocklink&action=edit blocklink]<br>
+[[MediaWiki_talk:Blocklink|Talk]]
+</td><td>
+block
+</td><td>
+{{int:Blocklink}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blocklistline&action=edit blocklistline]<br>
+[[MediaWiki_talk:Blocklistline|Talk]]
+</td><td>
+$1, $2 blocked $3 (expires $4)
+</td><td>
+{{int:Blocklistline}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blocklogentry&action=edit blocklogentry]<br>
+[[MediaWiki_talk:Blocklogentry|Talk]]
+</td><td>
+blocked &quot;$1&quot; with an expiry time of $2
+</td><td>
+{{int:Blocklogentry}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blocklogpage&action=edit blocklogpage]<br>
+[[MediaWiki_talk:Blocklogpage|Talk]]
+</td><td>
+Block_log
+</td><td>
+{{int:Blocklogpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Blocklogtext&action=edit blocklogtext]<br>
+[[MediaWiki_talk:Blocklogtext|Talk]]
+</td><td>
+This is a log of user blocking and unblocking actions. Automatically
+blocked IP addresses are not be listed. See the &#91;&#91;Special:Ipblocklist&#124;IP block list]] for
+the list of currently operational bans and blocks.
+</td><td>
+{{int:Blocklogtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bold_sample&action=edit bold_sample]<br>
+[[MediaWiki_talk:Bold_sample|Talk]]
+</td><td>
+Bold text
+</td><td>
+{{int:Bold_sample}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bold_tip&action=edit bold_tip]<br>
+[[MediaWiki_talk:Bold_tip|Talk]]
+</td><td>
+Bold text
+</td><td>
+{{int:Bold_tip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Booksources&action=edit booksources]<br>
+[[MediaWiki_talk:Booksources|Talk]]
+</td><td>
+Book sources
+</td><td>
+{{int:Booksources}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Booksourcetext&action=edit booksourcetext]<br>
+[[MediaWiki_talk:Booksourcetext|Talk]]
+</td><td>
+Below is a list of links to other sites that
+sell new and used books, and may also have further information
+about books you are looking for.Wiktionary is not affiliated with any of these businesses, and
+this list should not be construed as an endorsement.
+</td><td>
+{{int:Booksourcetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Brokenredirects&action=edit brokenredirects]<br>
+[[MediaWiki_talk:Brokenredirects|Talk]]
+</td><td>
+Broken Redirects
+</td><td>
+{{int:Brokenredirects}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Brokenredirectstext&action=edit brokenredirectstext]<br>
+[[MediaWiki_talk:Brokenredirectstext|Talk]]
+</td><td>
+The following redirects link to a non-existing pages.
+</td><td>
+{{int:Brokenredirectstext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bugreports&action=edit bugreports]<br>
+[[MediaWiki_talk:Bugreports|Talk]]
+</td><td>
+Bug reports
+</td><td>
+{{int:Bugreports}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bugreportspage&action=edit bugreportspage]<br>
+[[MediaWiki_talk:Bugreportspage|Talk]]
+</td><td>
+Wiktionary:Bug_reports
+</td><td>
+{{int:Bugreportspage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bureaucratlog&action=edit bureaucratlog]<br>
+[[MediaWiki_talk:Bureaucratlog|Talk]]
+</td><td>
+Bureaucrat_log
+</td><td>
+{{int:Bureaucratlog}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bureaucratlogentry&action=edit bureaucratlogentry]<br>
+[[MediaWiki_talk:Bureaucratlogentry|Talk]]
+</td><td>
+Rights for user &quot;$1&quot; set &quot;$2&quot;
+</td><td>
+{{int:Bureaucratlogentry}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bureaucrattext&action=edit bureaucrattext]<br>
+[[MediaWiki_talk:Bureaucrattext|Talk]]
+</td><td>
+The action you have requested can only be
+performed by sysops with &quot;bureaucrat&quot; status.
+</td><td>
+{{int:Bureaucrattext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bureaucrattitle&action=edit bureaucrattitle]<br>
+[[MediaWiki_talk:Bureaucrattitle|Talk]]
+</td><td>
+Bureaucrat access required
+</td><td>
+{{int:Bureaucrattitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bydate&action=edit bydate]<br>
+[[MediaWiki_talk:Bydate|Talk]]
+</td><td>
+by date
+</td><td>
+{{int:Bydate}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Byname&action=edit byname]<br>
+[[MediaWiki_talk:Byname|Talk]]
+</td><td>
+by name
+</td><td>
+{{int:Byname}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Bysize&action=edit bysize]<br>
+[[MediaWiki_talk:Bysize|Talk]]
+</td><td>
+by size
+</td><td>
+{{int:Bysize}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Cachederror&action=edit cachederror]<br>
+[[MediaWiki_talk:Cachederror|Talk]]
+</td><td>
+The following is a cached copy of the requested page, and may not be up to date.
+</td><td>
+{{int:Cachederror}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Cancel&action=edit cancel]<br>
+[[MediaWiki_talk:Cancel|Talk]]
+</td><td>
+Cancel
+</td><td>
+{{int:Cancel}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Cannotdelete&action=edit cannotdelete]<br>
+[[MediaWiki_talk:Cannotdelete|Talk]]
+</td><td>
+Could not delete the page or image specified. (It may have already been deleted by someone else.)
+</td><td>
+{{int:Cannotdelete}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Cantrollback&action=edit cantrollback]<br>
+[[MediaWiki_talk:Cantrollback|Talk]]
+</td><td>
+Cannot revert edit; last contributor is only author of this page.
+</td><td>
+{{int:Cantrollback}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Categories&action=edit categories]<br>
+[[MediaWiki_talk:Categories|Talk]]
+</td><td>
+Categories
+</td><td>
+{{int:Categories}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Category&action=edit category]<br>
+[[MediaWiki_talk:Category|Talk]]
+</td><td>
+category
+</td><td>
+{{int:Category}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Category_header&action=edit category_header]<br>
+[[MediaWiki_talk:Category_header|Talk]]
+</td><td>
+Articles in category &quot;$1&quot;
+</td><td>
+{{int:Category_header}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Changepassword&action=edit changepassword]<br>
+[[MediaWiki_talk:Changepassword|Talk]]
+</td><td>
+Change password
+</td><td>
+{{int:Changepassword}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Changes&action=edit changes]<br>
+[[MediaWiki_talk:Changes|Talk]]
+</td><td>
+changes
+</td><td>
+{{int:Changes}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Columns&action=edit columns]<br>
+[[MediaWiki_talk:Columns|Talk]]
+</td><td>
+Columns
+</td><td>
+{{int:Columns}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Commentedit&action=edit commentedit]<br>
+[[MediaWiki_talk:Commentedit|Talk]]
+</td><td>
+ (comment)
+</td><td>
+{{int:Commentedit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Compareselectedversions&action=edit compareselectedversions]<br>
+[[MediaWiki_talk:Compareselectedversions|Talk]]
+</td><td>
+Compare selected versions
+</td><td>
+{{int:Compareselectedversions}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirm&action=edit confirm]<br>
+[[MediaWiki_talk:Confirm|Talk]]
+</td><td>
+Confirm
+</td><td>
+{{int:Confirm}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmcheck&action=edit confirmcheck]<br>
+[[MediaWiki_talk:Confirmcheck|Talk]]
+</td><td>
+Yes, I really want to delete this.
+</td><td>
+{{int:Confirmcheck}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmdelete&action=edit confirmdelete]<br>
+[[MediaWiki_talk:Confirmdelete|Talk]]
+</td><td>
+Confirm delete
+</td><td>
+{{int:Confirmdelete}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmdeletetext&action=edit confirmdeletetext]<br>
+[[MediaWiki_talk:Confirmdeletetext|Talk]]
+</td><td>
+You are about to permanently delete a page
+or image along with all of its history from the database.
+Please confirm that you intend to do this, that you understand the
+consequences, and that you are doing this in accordance with
+&#91;&#91;Wiktionary:Policy]].
+</td><td>
+{{int:Confirmdeletetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmprotect&action=edit confirmprotect]<br>
+[[MediaWiki_talk:Confirmprotect|Talk]]
+</td><td>
+Confirm protection
+</td><td>
+{{int:Confirmprotect}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmprotecttext&action=edit confirmprotecttext]<br>
+[[MediaWiki_talk:Confirmprotecttext|Talk]]
+</td><td>
+Do you really want to protect this page?
+</td><td>
+{{int:Confirmprotecttext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmunprotect&action=edit confirmunprotect]<br>
+[[MediaWiki_talk:Confirmunprotect|Talk]]
+</td><td>
+Confirm unprotection
+</td><td>
+{{int:Confirmunprotect}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Confirmunprotecttext&action=edit confirmunprotecttext]<br>
+[[MediaWiki_talk:Confirmunprotecttext|Talk]]
+</td><td>
+Do you really want to unprotect this page?
+</td><td>
+{{int:Confirmunprotecttext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Contextchars&action=edit contextchars]<br>
+[[MediaWiki_talk:Contextchars|Talk]]
+</td><td>
+Characters of context per line
+</td><td>
+{{int:Contextchars}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Contextlines&action=edit contextlines]<br>
+[[MediaWiki_talk:Contextlines|Talk]]
+</td><td>
+Lines to show per hit
+</td><td>
+{{int:Contextlines}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Contribslink&action=edit contribslink]<br>
+[[MediaWiki_talk:Contribslink|Talk]]
+</td><td>
+contribs
+</td><td>
+{{int:Contribslink}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Contribsub&action=edit contribsub]<br>
+[[MediaWiki_talk:Contribsub|Talk]]
+</td><td>
+For $1
+</td><td>
+{{int:Contribsub}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Contributions&action=edit contributions]<br>
+[[MediaWiki_talk:Contributions|Talk]]
+</td><td>
+User contributions
+</td><td>
+{{int:Contributions}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Copyright&action=edit copyright]<br>
+[[MediaWiki_talk:Copyright|Talk]]
+</td><td>
+Content is available under $1.
+</td><td>
+{{int:Copyright}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Copyrightpage&action=edit copyrightpage]<br>
+[[MediaWiki_talk:Copyrightpage|Talk]]
+</td><td>
+Wiktionary:Copyrights
+</td><td>
+{{int:Copyrightpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Copyrightpagename&action=edit copyrightpagename]<br>
+[[MediaWiki_talk:Copyrightpagename|Talk]]
+</td><td>
+Wiktionary copyright
+</td><td>
+{{int:Copyrightpagename}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Copyrightwarning&action=edit copyrightwarning]<br>
+[[MediaWiki_talk:Copyrightwarning|Talk]]
+</td><td>
+Please note that all contributions to Wiktionary are
+considered to be released under the GNU Free Documentation License
+(see $1 for details).
+If you don&#39;t want your writing to be edited mercilessly and redistributed
+at will, then don&#39;t submit it here.&lt;br /&gt;
+You are also promising us that you wrote this yourself, or copied it from a
+public domain or similar free resource.
+&lt;strong&gt;DO NOT SUBMIT COPYRIGHTED WORK WITHOUT PERMISSION!&lt;/strong&gt;
+</td><td>
+{{int:Copyrightwarning}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Couldntremove&action=edit couldntremove]<br>
+[[MediaWiki_talk:Couldntremove|Talk]]
+</td><td>
+Couldn&#39;t remove item &#39;$1&#39;...
+</td><td>
+{{int:Couldntremove}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Createaccount&action=edit createaccount]<br>
+[[MediaWiki_talk:Createaccount|Talk]]
+</td><td>
+Create new account
+</td><td>
+{{int:Createaccount}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Createaccountmail&action=edit createaccountmail]<br>
+[[MediaWiki_talk:Createaccountmail|Talk]]
+</td><td>
+by email
+</td><td>
+{{int:Createaccountmail}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Cur&action=edit cur]<br>
+[[MediaWiki_talk:Cur|Talk]]
+</td><td>
+cur
+</td><td>
+{{int:Cur}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Currentevents&action=edit currentevents]<br>
+[[MediaWiki_talk:Currentevents|Talk]]
+</td><td>
+Current events
+</td><td>
+{{int:Currentevents}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Currentrev&action=edit currentrev]<br>
+[[MediaWiki_talk:Currentrev|Talk]]
+</td><td>
+Current revision
+</td><td>
+{{int:Currentrev}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Databaseerror&action=edit databaseerror]<br>
+[[MediaWiki_talk:Databaseerror|Talk]]
+</td><td>
+Database error
+</td><td>
+{{int:Databaseerror}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Dateformat&action=edit dateformat]<br>
+[[MediaWiki_talk:Dateformat|Talk]]
+</td><td>
+Date format
+</td><td>
+{{int:Dateformat}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Dberrortext&action=edit dberrortext]<br>
+[[MediaWiki_talk:Dberrortext|Talk]]
+</td><td>
+A database query syntax error has occurred.
+This could be because of an illegal search query (see $5),
+or it may indicate a bug in the software.
+The last attempted database query was:
+&lt;blockquote&gt;&lt;tt&gt;$1&lt;/tt&gt;&lt;/blockquote&gt;
+from within function &quot;&lt;tt&gt;$2&lt;/tt&gt;&quot;.
+MySQL returned error &quot;&lt;tt&gt;$3: $4&lt;/tt&gt;&quot;.
+</td><td>
+{{int:Dberrortext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Dberrortextcl&action=edit dberrortextcl]<br>
+[[MediaWiki_talk:Dberrortextcl|Talk]]
+</td><td>
+A database query syntax error has occurred.
+The last attempted database query was:
+&quot;$1&quot;
+from within function &quot;$2&quot;.
+MySQL returned error &quot;$3: $4&quot;.
+
+</td><td>
+{{int:Dberrortextcl}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deadendpages&action=edit deadendpages]<br>
+[[MediaWiki_talk:Deadendpages|Talk]]
+</td><td>
+Dead-end pages
+</td><td>
+{{int:Deadendpages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Debug&action=edit debug]<br>
+[[MediaWiki_talk:Debug|Talk]]
+</td><td>
+Debug
+</td><td>
+{{int:Debug}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Defaultns&action=edit defaultns]<br>
+[[MediaWiki_talk:Defaultns|Talk]]
+</td><td>
+Search in these namespaces by default:
+</td><td>
+{{int:Defaultns}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Defemailsubject&action=edit defemailsubject]<br>
+[[MediaWiki_talk:Defemailsubject|Talk]]
+</td><td>
+Wiktionary e-mail
+</td><td>
+{{int:Defemailsubject}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Delete&action=edit delete]<br>
+[[MediaWiki_talk:Delete|Talk]]
+</td><td>
+Delete
+</td><td>
+{{int:Delete}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletecomment&action=edit deletecomment]<br>
+[[MediaWiki_talk:Deletecomment|Talk]]
+</td><td>
+Reason for deletion
+</td><td>
+{{int:Deletecomment}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletedarticle&action=edit deletedarticle]<br>
+[[MediaWiki_talk:Deletedarticle|Talk]]
+</td><td>
+deleted &quot;$1&quot;
+</td><td>
+{{int:Deletedarticle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletedtext&action=edit deletedtext]<br>
+[[MediaWiki_talk:Deletedtext|Talk]]
+</td><td>
+&quot;$1&quot; has been deleted.
+See $2 for a record of recent deletions.
+</td><td>
+{{int:Deletedtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deleteimg&action=edit deleteimg]<br>
+[[MediaWiki_talk:Deleteimg|Talk]]
+</td><td>
+del
+</td><td>
+{{int:Deleteimg}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletepage&action=edit deletepage]<br>
+[[MediaWiki_talk:Deletepage|Talk]]
+</td><td>
+Delete page
+</td><td>
+{{int:Deletepage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletesub&action=edit deletesub]<br>
+[[MediaWiki_talk:Deletesub|Talk]]
+</td><td>
+(Deleting &quot;$1&quot;)
+</td><td>
+{{int:Deletesub}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletethispage&action=edit deletethispage]<br>
+[[MediaWiki_talk:Deletethispage|Talk]]
+</td><td>
+Delete this page
+</td><td>
+{{int:Deletethispage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Deletionlog&action=edit deletionlog]<br>
+[[MediaWiki_talk:Deletionlog|Talk]]
+</td><td>
+deletion log
+</td><td>
+{{int:Deletionlog}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Dellogpage&action=edit dellogpage]<br>
+[[MediaWiki_talk:Dellogpage|Talk]]
+</td><td>
+Deletion_log
+</td><td>
+{{int:Dellogpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Dellogpagetext&action=edit dellogpagetext]<br>
+[[MediaWiki_talk:Dellogpagetext|Talk]]
+</td><td>
+Below is a list of the most recent deletions.
+All times shown are server time (UTC).
+&lt;ul&gt;
+&lt;/ul&gt;
+
+</td><td>
+{{int:Dellogpagetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Developerspheading&action=edit developerspheading]<br>
+[[MediaWiki_talk:Developerspheading|Talk]]
+</td><td>
+For developer use only
+</td><td>
+{{int:Developerspheading}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Developertext&action=edit developertext]<br>
+[[MediaWiki_talk:Developertext|Talk]]
+</td><td>
+The action you have requested can only be
+performed by users with &quot;developer&quot; status.
+See $1.
+</td><td>
+{{int:Developertext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Developertitle&action=edit developertitle]<br>
+[[MediaWiki_talk:Developertitle|Talk]]
+</td><td>
+Developer access required
+</td><td>
+{{int:Developertitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Diff&action=edit diff]<br>
+[[MediaWiki_talk:Diff|Talk]]
+</td><td>
+diff
+</td><td>
+{{int:Diff}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Difference&action=edit difference]<br>
+[[MediaWiki_talk:Difference|Talk]]
+</td><td>
+(Difference between revisions)
+</td><td>
+{{int:Difference}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Disambiguations&action=edit disambiguations]<br>
+[[MediaWiki_talk:Disambiguations|Talk]]
+</td><td>
+Disambiguation pages
+</td><td>
+{{int:Disambiguations}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Disambiguationspage&action=edit disambiguationspage]<br>
+[[MediaWiki_talk:Disambiguationspage|Talk]]
+</td><td>
+Wiktionary:Links_to_disambiguating_pages
+</td><td>
+{{int:Disambiguationspage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Disambiguationstext&action=edit disambiguationstext]<br>
+[[MediaWiki_talk:Disambiguationstext|Talk]]
+</td><td>
+The following pages link to a &lt;i&gt;disambiguation page&lt;/i&gt;. They should link to the appropriate topic instead.&lt;br /&gt;A page is treated as dismbiguation if it is linked from $1.&lt;br /&gt;Links from other namespaces are &lt;i&gt;not&lt;/i&gt; listed here.
+</td><td>
+{{int:Disambiguationstext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Disclaimerpage&action=edit disclaimerpage]<br>
+[[MediaWiki_talk:Disclaimerpage|Talk]]
+</td><td>
+Wiktionary:General_disclaimer
+</td><td>
+{{int:Disclaimerpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Disclaimers&action=edit disclaimers]<br>
+[[MediaWiki_talk:Disclaimers|Talk]]
+</td><td>
+Disclaimers
+</td><td>
+{{int:Disclaimers}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Doubleredirects&action=edit doubleredirects]<br>
+[[MediaWiki_talk:Doubleredirects|Talk]]
+</td><td>
+Double Redirects
+</td><td>
+{{int:Doubleredirects}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Doubleredirectstext&action=edit doubleredirectstext]<br>
+[[MediaWiki_talk:Doubleredirectstext|Talk]]
+</td><td>
+&lt;b&gt;Attention:&lt;/b&gt; This list may contain false positives. That usually means there is additional text with links below the first #REDIRECT.&lt;br /&gt;
+Each row contains links to the first and second redirect, as well as the first line of the second redirect text, usually giving the &quot;real&quot; target page, which the first redirect should point to.
+</td><td>
+{{int:Doubleredirectstext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Edit&action=edit edit]<br>
+[[MediaWiki_talk:Edit|Talk]]
+</td><td>
+Edit
+</td><td>
+{{int:Edit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editcomment&action=edit editcomment]<br>
+[[MediaWiki_talk:Editcomment|Talk]]
+</td><td>
+The edit comment was: &quot;&lt;i&gt;$1&lt;/i&gt;&quot;.
+</td><td>
+{{int:Editcomment}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editconflict&action=edit editconflict]<br>
+[[MediaWiki_talk:Editconflict|Talk]]
+</td><td>
+Edit conflict: $1
+</td><td>
+{{int:Editconflict}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editcurrent&action=edit editcurrent]<br>
+[[MediaWiki_talk:Editcurrent|Talk]]
+</td><td>
+Edit the current version of this page
+</td><td>
+{{int:Editcurrent}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Edithelp&action=edit edithelp]<br>
+[[MediaWiki_talk:Edithelp|Talk]]
+</td><td>
+Editing help
+</td><td>
+{{int:Edithelp}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Edithelppage&action=edit edithelppage]<br>
+[[MediaWiki_talk:Edithelppage|Talk]]
+</td><td>
+Help:Editing
+</td><td>
+{{int:Edithelppage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editing&action=edit editing]<br>
+[[MediaWiki_talk:Editing|Talk]]
+</td><td>
+Editing $1
+</td><td>
+{{int:Editing}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editingold&action=edit editingold]<br>
+[[MediaWiki_talk:Editingold|Talk]]
+</td><td>
+&lt;strong&gt;WARNING: You are editing an out-of-date
+revision of this page.
+If you save it, any changes made since this revision will be lost.&lt;/strong&gt;
+
+</td><td>
+{{int:Editingold}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editsection&action=edit editsection]<br>
+[[MediaWiki_talk:Editsection|Talk]]
+</td><td>
+edit
+</td><td>
+{{int:Editsection}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Editthispage&action=edit editthispage]<br>
+[[MediaWiki_talk:Editthispage|Talk]]
+</td><td>
+Edit this page
+</td><td>
+{{int:Editthispage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailflag&action=edit emailflag]<br>
+[[MediaWiki_talk:Emailflag|Talk]]
+</td><td>
+Disable e-mail from other users
+</td><td>
+{{int:Emailflag}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailforlost&action=edit emailforlost]<br>
+[[MediaWiki_talk:Emailforlost|Talk]]
+</td><td>
+Fields marked with a star (*) are optional. Storing an email address enables people to contact you through the website without you having to reveal your
+email address to them, and it can be used to send you a new password if you forget it.&lt;br /&gt;&lt;br /&gt;Your real name, if you choose to provide it, will be used for giving you attribution for your work.
+</td><td>
+{{int:Emailforlost}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailfrom&action=edit emailfrom]<br>
+[[MediaWiki_talk:Emailfrom|Talk]]
+</td><td>
+From
+</td><td>
+{{int:Emailfrom}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailmessage&action=edit emailmessage]<br>
+[[MediaWiki_talk:Emailmessage|Talk]]
+</td><td>
+Message
+</td><td>
+{{int:Emailmessage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailpage&action=edit emailpage]<br>
+[[MediaWiki_talk:Emailpage|Talk]]
+</td><td>
+E-mail user
+</td><td>
+{{int:Emailpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailpagetext&action=edit emailpagetext]<br>
+[[MediaWiki_talk:Emailpagetext|Talk]]
+</td><td>
+If this user has entered a valid e-mail address in
+his or her user preferences, the form below will send a single message.
+The e-mail address you entered in your user preferences will appear
+as the &quot;From&quot; address of the mail, so the recipient will be able
+to reply.
+</td><td>
+{{int:Emailpagetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailsend&action=edit emailsend]<br>
+[[MediaWiki_talk:Emailsend|Talk]]
+</td><td>
+Send
+</td><td>
+{{int:Emailsend}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailsent&action=edit emailsent]<br>
+[[MediaWiki_talk:Emailsent|Talk]]
+</td><td>
+E-mail sent
+</td><td>
+{{int:Emailsent}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailsenttext&action=edit emailsenttext]<br>
+[[MediaWiki_talk:Emailsenttext|Talk]]
+</td><td>
+Your e-mail message has been sent.
+</td><td>
+{{int:Emailsenttext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailsubject&action=edit emailsubject]<br>
+[[MediaWiki_talk:Emailsubject|Talk]]
+</td><td>
+Subject
+</td><td>
+{{int:Emailsubject}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailto&action=edit emailto]<br>
+[[MediaWiki_talk:Emailto|Talk]]
+</td><td>
+To
+</td><td>
+{{int:Emailto}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Emailuser&action=edit emailuser]<br>
+[[MediaWiki_talk:Emailuser|Talk]]
+</td><td>
+E-mail this user
+</td><td>
+{{int:Emailuser}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Enterlockreason&action=edit enterlockreason]<br>
+[[MediaWiki_talk:Enterlockreason|Talk]]
+</td><td>
+Enter a reason for the lock, including an estimate
+of when the lock will be released
+</td><td>
+{{int:Enterlockreason}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Error&action=edit error]<br>
+[[MediaWiki_talk:Error|Talk]]
+</td><td>
+Error
+</td><td>
+{{int:Error}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Errorpagetitle&action=edit errorpagetitle]<br>
+[[MediaWiki_talk:Errorpagetitle|Talk]]
+</td><td>
+Error
+</td><td>
+{{int:Errorpagetitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Exbeforeblank&action=edit exbeforeblank]<br>
+[[MediaWiki_talk:Exbeforeblank|Talk]]
+</td><td>
+content before blanking was:
+</td><td>
+{{int:Exbeforeblank}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Exblank&action=edit exblank]<br>
+[[MediaWiki_talk:Exblank|Talk]]
+</td><td>
+page was empty
+</td><td>
+{{int:Exblank}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Excontent&action=edit excontent]<br>
+[[MediaWiki_talk:Excontent|Talk]]
+</td><td>
+content was:
+</td><td>
+{{int:Excontent}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Explainconflict&action=edit explainconflict]<br>
+[[MediaWiki_talk:Explainconflict|Talk]]
+</td><td>
+Someone else has changed this page since you
+started editing it.
+The upper text area contains the page text as it currently exists.
+Your changes are shown in the lower text area.
+You will have to merge your changes into the existing text.
+&lt;b&gt;Only&lt;/b&gt; the text in the upper text area will be saved when you
+press &quot;Save page&quot;.
+&lt;p&gt;
+</td><td>
+{{int:Explainconflict}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Export&action=edit export]<br>
+[[MediaWiki_talk:Export|Talk]]
+</td><td>
+Export pages
+</td><td>
+{{int:Export}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Exportcuronly&action=edit exportcuronly]<br>
+[[MediaWiki_talk:Exportcuronly|Talk]]
+</td><td>
+Include only the current revision, not the full history
+</td><td>
+{{int:Exportcuronly}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Exporttext&action=edit exporttext]<br>
+[[MediaWiki_talk:Exporttext|Talk]]
+</td><td>
+You can export the text and editing history of a particular
+page or set of pages wrapped in some XML; this can then be imported into another
+wiki running MediaWiki software, transformed, or just kept for your private
+amusement.
+</td><td>
+{{int:Exporttext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Extlink_sample&action=edit extlink_sample]<br>
+[[MediaWiki_talk:Extlink_sample|Talk]]
+</td><td>
+http&#58;//www.example.com link title
+</td><td>
+{{int:Extlink_sample}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Extlink_tip&action=edit extlink_tip]<br>
+[[MediaWiki_talk:Extlink_tip|Talk]]
+</td><td>
+External link (remember http&#58;// prefix)
+</td><td>
+{{int:Extlink_tip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Faq&action=edit faq]<br>
+[[MediaWiki_talk:Faq|Talk]]
+</td><td>
+FAQ
+</td><td>
+{{int:Faq}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Faqpage&action=edit faqpage]<br>
+[[MediaWiki_talk:Faqpage|Talk]]
+</td><td>
+Wiktionary:FAQ
+</td><td>
+{{int:Faqpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Feedlinks&action=edit feedlinks]<br>
+[[MediaWiki_talk:Feedlinks|Talk]]
+</td><td>
+Feed:
+</td><td>
+{{int:Feedlinks}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filecopyerror&action=edit filecopyerror]<br>
+[[MediaWiki_talk:Filecopyerror|Talk]]
+</td><td>
+Could not copy file &quot;$1&quot; to &quot;$2&quot;.
+</td><td>
+{{int:Filecopyerror}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filedeleteerror&action=edit filedeleteerror]<br>
+[[MediaWiki_talk:Filedeleteerror|Talk]]
+</td><td>
+Could not delete file &quot;$1&quot;.
+</td><td>
+{{int:Filedeleteerror}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filedesc&action=edit filedesc]<br>
+[[MediaWiki_talk:Filedesc|Talk]]
+</td><td>
+Summary
+</td><td>
+{{int:Filedesc}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filename&action=edit filename]<br>
+[[MediaWiki_talk:Filename|Talk]]
+</td><td>
+Filename
+</td><td>
+{{int:Filename}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filenotfound&action=edit filenotfound]<br>
+[[MediaWiki_talk:Filenotfound|Talk]]
+</td><td>
+Could not find file &quot;$1&quot;.
+</td><td>
+{{int:Filenotfound}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filerenameerror&action=edit filerenameerror]<br>
+[[MediaWiki_talk:Filerenameerror|Talk]]
+</td><td>
+Could not rename file &quot;$1&quot; to &quot;$2&quot;.
+</td><td>
+{{int:Filerenameerror}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filesource&action=edit filesource]<br>
+[[MediaWiki_talk:Filesource|Talk]]
+</td><td>
+Source
+</td><td>
+{{int:Filesource}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Filestatus&action=edit filestatus]<br>
+[[MediaWiki_talk:Filestatus|Talk]]
+</td><td>
+Copyright status
+</td><td>
+{{int:Filestatus}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Fileuploaded&action=edit fileuploaded]<br>
+[[MediaWiki_talk:Fileuploaded|Talk]]
+</td><td>
+File &quot;$1&quot; uploaded successfully.
+Please follow this link: $2 to the description page and fill
+in information about the file, such as where it came from, when it was
+created and by whom, and anything else you may know about it.
+</td><td>
+{{int:Fileuploaded}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Formerror&action=edit formerror]<br>
+[[MediaWiki_talk:Formerror|Talk]]
+</td><td>
+Error: could not submit form
+</td><td>
+{{int:Formerror}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Fromwikipedia&action=edit fromwikipedia]<br>
+[[MediaWiki_talk:Fromwikipedia|Talk]]
+</td><td>
+From Wiktionary
+</td><td>
+{{int:Fromwikipedia}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Getimagelist&action=edit getimagelist]<br>
+[[MediaWiki_talk:Getimagelist|Talk]]
+</td><td>
+fetching image list
+</td><td>
+{{int:Getimagelist}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Go&action=edit go]<br>
+[[MediaWiki_talk:Go|Talk]]
+</td><td>
+Go
+</td><td>
+{{int:Go}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Googlesearch&action=edit googlesearch]<br>
+[[MediaWiki_talk:Googlesearch|Talk]]
+</td><td>
+
+&lt;!-- SiteSearch Google --&gt;
+&lt;FORM method=GET action=&quot;http&#58;//www.google.com/search&quot;&gt;
+&lt;TABLE bgcolor=&quot;#FFFFFF&quot;&gt;&lt;tr&gt;&lt;td&gt;
+&lt;A HREF=&quot;http&#58;//www.google.com/&quot;&gt;
+&lt;IMG SRC=&quot;http&#58;//www.google.com/logos/Logo_40wht.gif&quot;
+border=&quot;0&quot; ALT=&quot;Google&quot;&gt;&lt;/A&gt;
+&lt;/td&gt;
+&lt;td&gt;
+&lt;INPUT TYPE=text name=q size=31 maxlength=255 value=&quot;$1&quot;&gt;
+&lt;INPUT type=submit name=btnG VALUE=&quot;Google Search&quot;&gt;
+&lt;font size=-1&gt;
+&lt;input type=hidden name=domains value=&quot;http&#58;//tl.wiktionary.org&quot;&gt;&lt;br /&gt;&lt;input type=radio name=sitesearch value=&quot;&quot;&gt; WWW &lt;input type=radio name=sitesearch value=&quot;http&#58;//tl.wiktionary.org&quot; checked&gt; http&#58;//tl.wiktionary.org &lt;br /&gt;
+&lt;input type=&#39;hidden&#39; name=&#39;ie&#39; value=&#39;$2&#39;&gt;
+&lt;input type=&#39;hidden&#39; name=&#39;oe&#39; value=&#39;$2&#39;&gt;
+&lt;/font&gt;
+&lt;/td&gt;&lt;/tr&gt;&lt;/TABLE&gt;
+&lt;/FORM&gt;
+&lt;!-- SiteSearch Google --&gt;
+</td><td>
+{{int:Googlesearch}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Guesstimezone&action=edit guesstimezone]<br>
+[[MediaWiki_talk:Guesstimezone|Talk]]
+</td><td>
+Fill in from browser
+</td><td>
+{{int:Guesstimezone}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Headline_sample&action=edit headline_sample]<br>
+[[MediaWiki_talk:Headline_sample|Talk]]
+</td><td>
+Headline text
+</td><td>
+{{int:Headline_sample}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Headline_tip&action=edit headline_tip]<br>
+[[MediaWiki_talk:Headline_tip|Talk]]
+</td><td>
+Level 2 headline
+</td><td>
+{{int:Headline_tip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Help&action=edit help]<br>
+[[MediaWiki_talk:Help|Talk]]
+</td><td>
+Help
+</td><td>
+{{int:Help}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Helppage&action=edit helppage]<br>
+[[MediaWiki_talk:Helppage|Talk]]
+</td><td>
+Help:Contents
+</td><td>
+{{int:Helppage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Hide&action=edit hide]<br>
+[[MediaWiki_talk:Hide|Talk]]
+</td><td>
+hide
+</td><td>
+{{int:Hide}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Hidetoc&action=edit hidetoc]<br>
+[[MediaWiki_talk:Hidetoc|Talk]]
+</td><td>
+hide
+</td><td>
+{{int:Hidetoc}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Hist&action=edit hist]<br>
+[[MediaWiki_talk:Hist|Talk]]
+</td><td>
+hist
+</td><td>
+{{int:Hist}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Histlegend&action=edit histlegend]<br>
+[[MediaWiki_talk:Histlegend|Talk]]
+</td><td>
+Diff selection: mark the radio boxes of the versions to compare and hit enter or the button at the bottom.&lt;br/&gt;
+Legend: (cur) = difference with current version,
+(last) = difference with preceding version, M = minor edit.
+</td><td>
+{{int:Histlegend}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:History&action=edit history]<br>
+[[MediaWiki_talk:History|Talk]]
+</td><td>
+Page history
+</td><td>
+{{int:History}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:History_short&action=edit history_short]<br>
+[[MediaWiki_talk:History_short|Talk]]
+</td><td>
+History
+</td><td>
+{{int:History_short}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Historywarning&action=edit historywarning]<br>
+[[MediaWiki_talk:Historywarning|Talk]]
+</td><td>
+Warning: The page you are about to delete has a history:
+</td><td>
+{{int:Historywarning}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Hr_tip&action=edit hr_tip]<br>
+[[MediaWiki_talk:Hr_tip|Talk]]
+</td><td>
+Horizontal line (use sparingly)
+</td><td>
+{{int:Hr_tip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ignorewarning&action=edit ignorewarning]<br>
+[[MediaWiki_talk:Ignorewarning|Talk]]
+</td><td>
+Ignore warning and save file anyway.
+</td><td>
+{{int:Ignorewarning}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ilshowmatch&action=edit ilshowmatch]<br>
+[[MediaWiki_talk:Ilshowmatch|Talk]]
+</td><td>
+Show all images with names matching
+</td><td>
+{{int:Ilshowmatch}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ilsubmit&action=edit ilsubmit]<br>
+[[MediaWiki_talk:Ilsubmit|Talk]]
+</td><td>
+Search
+</td><td>
+{{int:Ilsubmit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Image_sample&action=edit image_sample]<br>
+[[MediaWiki_talk:Image_sample|Talk]]
+</td><td>
+Example.jpg
+</td><td>
+{{int:Image_sample}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Image_tip&action=edit image_tip]<br>
+[[MediaWiki_talk:Image_tip|Talk]]
+</td><td>
+Embedded image
+</td><td>
+{{int:Image_tip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imagelinks&action=edit imagelinks]<br>
+[[MediaWiki_talk:Imagelinks|Talk]]
+</td><td>
+Image links
+</td><td>
+{{int:Imagelinks}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imagelist&action=edit imagelist]<br>
+[[MediaWiki_talk:Imagelist|Talk]]
+</td><td>
+Image list
+</td><td>
+{{int:Imagelist}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imagelisttext&action=edit imagelisttext]<br>
+[[MediaWiki_talk:Imagelisttext|Talk]]
+</td><td>
+Below is a list of $1 images sorted $2.
+</td><td>
+{{int:Imagelisttext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imagepage&action=edit imagepage]<br>
+[[MediaWiki_talk:Imagepage|Talk]]
+</td><td>
+View image page
+</td><td>
+{{int:Imagepage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imagereverted&action=edit imagereverted]<br>
+[[MediaWiki_talk:Imagereverted|Talk]]
+</td><td>
+Revert to earlier version was successful.
+</td><td>
+{{int:Imagereverted}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imgdelete&action=edit imgdelete]<br>
+[[MediaWiki_talk:Imgdelete|Talk]]
+</td><td>
+del
+</td><td>
+{{int:Imgdelete}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imgdesc&action=edit imgdesc]<br>
+[[MediaWiki_talk:Imgdesc|Talk]]
+</td><td>
+desc
+</td><td>
+{{int:Imgdesc}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imghistlegend&action=edit imghistlegend]<br>
+[[MediaWiki_talk:Imghistlegend|Talk]]
+</td><td>
+Legend: (cur) = this is the current image, (del) = delete
+this old version, (rev) = revert to this old version.
+&lt;br /&gt;&lt;i&gt;Click on date to see image uploaded on that date&lt;/i&gt;.
+</td><td>
+{{int:Imghistlegend}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imghistory&action=edit imghistory]<br>
+[[MediaWiki_talk:Imghistory|Talk]]
+</td><td>
+Image history
+</td><td>
+{{int:Imghistory}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Imglegend&action=edit imglegend]<br>
+[[MediaWiki_talk:Imglegend|Talk]]
+</td><td>
+Legend: (desc) = show/edit image description.
+</td><td>
+{{int:Imglegend}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Import&action=edit import]<br>
+[[MediaWiki_talk:Import|Talk]]
+</td><td>
+Import pages
+</td><td>
+{{int:Import}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Importfailed&action=edit importfailed]<br>
+[[MediaWiki_talk:Importfailed|Talk]]
+</td><td>
+Import failed: $1
+</td><td>
+{{int:Importfailed}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Importhistoryconflict&action=edit importhistoryconflict]<br>
+[[MediaWiki_talk:Importhistoryconflict|Talk]]
+</td><td>
+Conflicting history revision exists (may have imported this page before)
+</td><td>
+{{int:Importhistoryconflict}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Importnotext&action=edit importnotext]<br>
+[[MediaWiki_talk:Importnotext|Talk]]
+</td><td>
+Empty or no text
+</td><td>
+{{int:Importnotext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Importsuccess&action=edit importsuccess]<br>
+[[MediaWiki_talk:Importsuccess|Talk]]
+</td><td>
+Import succeeded!
+</td><td>
+{{int:Importsuccess}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Importtext&action=edit importtext]<br>
+[[MediaWiki_talk:Importtext|Talk]]
+</td><td>
+Please export the file from the source wiki using the Special:Export utility, save it to your disk and upload it here.
+</td><td>
+{{int:Importtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Infobox&action=edit infobox]<br>
+[[MediaWiki_talk:Infobox|Talk]]
+</td><td>
+Click a button to get an example text
+</td><td>
+{{int:Infobox}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Infobox_alert&action=edit infobox_alert]<br>
+[[MediaWiki_talk:Infobox_alert|Talk]]
+</td><td>
+Please enter the text you want to be formatted.\n It will be shown in the infobox for copy and pasting.\nExample:\n$1\nwill become:\n$2
+</td><td>
+{{int:Infobox_alert}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Internalerror&action=edit internalerror]<br>
+[[MediaWiki_talk:Internalerror|Talk]]
+</td><td>
+Internal error
+</td><td>
+{{int:Internalerror}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Intl&action=edit intl]<br>
+[[MediaWiki_talk:Intl|Talk]]
+</td><td>
+Interlanguage links
+</td><td>
+{{int:Intl}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ip_range_invalid&action=edit ip_range_invalid]<br>
+[[MediaWiki_talk:Ip_range_invalid|Talk]]
+</td><td>
+Invalid IP range.
+
+</td><td>
+{{int:Ip_range_invalid}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipaddress&action=edit ipaddress]<br>
+[[MediaWiki_talk:Ipaddress|Talk]]
+</td><td>
+IP Address/username
+</td><td>
+{{int:Ipaddress}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipb_expiry_invalid&action=edit ipb_expiry_invalid]<br>
+[[MediaWiki_talk:Ipb_expiry_invalid|Talk]]
+</td><td>
+Expiry time invalid.
+</td><td>
+{{int:Ipb_expiry_invalid}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipbexpiry&action=edit ipbexpiry]<br>
+[[MediaWiki_talk:Ipbexpiry|Talk]]
+</td><td>
+Expiry
+</td><td>
+{{int:Ipbexpiry}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipblocklist&action=edit ipblocklist]<br>
+[[MediaWiki_talk:Ipblocklist|Talk]]
+</td><td>
+List of blocked IP addresses and usernames
+</td><td>
+{{int:Ipblocklist}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipbreason&action=edit ipbreason]<br>
+[[MediaWiki_talk:Ipbreason|Talk]]
+</td><td>
+Reason
+</td><td>
+{{int:Ipbreason}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipbsubmit&action=edit ipbsubmit]<br>
+[[MediaWiki_talk:Ipbsubmit|Talk]]
+</td><td>
+Block this user
+</td><td>
+{{int:Ipbsubmit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipusubmit&action=edit ipusubmit]<br>
+[[MediaWiki_talk:Ipusubmit|Talk]]
+</td><td>
+Unblock this address
+</td><td>
+{{int:Ipusubmit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ipusuccess&action=edit ipusuccess]<br>
+[[MediaWiki_talk:Ipusuccess|Talk]]
+</td><td>
+&quot;$1&quot; unblocked
+</td><td>
+{{int:Ipusuccess}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Isbn&action=edit isbn]<br>
+[[MediaWiki_talk:Isbn|Talk]]
+</td><td>
+ISBN
+</td><td>
+{{int:Isbn}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Isredirect&action=edit isredirect]<br>
+[[MediaWiki_talk:Isredirect|Talk]]
+</td><td>
+redirect page
+</td><td>
+{{int:Isredirect}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Italic_sample&action=edit italic_sample]<br>
+[[MediaWiki_talk:Italic_sample|Talk]]
+</td><td>
+Italic text
+</td><td>
+{{int:Italic_sample}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Italic_tip&action=edit italic_tip]<br>
+[[MediaWiki_talk:Italic_tip|Talk]]
+</td><td>
+Italic text
+</td><td>
+{{int:Italic_tip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Iteminvalidname&action=edit iteminvalidname]<br>
+[[MediaWiki_talk:Iteminvalidname|Talk]]
+</td><td>
+Problem with item &#39;$1&#39;, invalid name...
+</td><td>
+{{int:Iteminvalidname}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Largefile&action=edit largefile]<br>
+[[MediaWiki_talk:Largefile|Talk]]
+</td><td>
+It is recommended that images not exceed 100k in size.
+</td><td>
+{{int:Largefile}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Last&action=edit last]<br>
+[[MediaWiki_talk:Last|Talk]]
+</td><td>
+last
+</td><td>
+{{int:Last}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lastmodified&action=edit lastmodified]<br>
+[[MediaWiki_talk:Lastmodified|Talk]]
+</td><td>
+This page was last modified $1.
+</td><td>
+{{int:Lastmodified}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lastmodifiedby&action=edit lastmodifiedby]<br>
+[[MediaWiki_talk:Lastmodifiedby|Talk]]
+</td><td>
+This page was last modified $1 by $2.
+</td><td>
+{{int:Lastmodifiedby}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lineno&action=edit lineno]<br>
+[[MediaWiki_talk:Lineno|Talk]]
+</td><td>
+Line $1:
+</td><td>
+{{int:Lineno}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Link_sample&action=edit link_sample]<br>
+[[MediaWiki_talk:Link_sample|Talk]]
+</td><td>
+Link title
+</td><td>
+{{int:Link_sample}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Link_tip&action=edit link_tip]<br>
+[[MediaWiki_talk:Link_tip|Talk]]
+</td><td>
+Internal link
+</td><td>
+{{int:Link_tip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Linklistsub&action=edit linklistsub]<br>
+[[MediaWiki_talk:Linklistsub|Talk]]
+</td><td>
+(List of links)
+</td><td>
+{{int:Linklistsub}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Linkshere&action=edit linkshere]<br>
+[[MediaWiki_talk:Linkshere|Talk]]
+</td><td>
+The following pages link to here:
+</td><td>
+{{int:Linkshere}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Linkstoimage&action=edit linkstoimage]<br>
+[[MediaWiki_talk:Linkstoimage|Talk]]
+</td><td>
+The following pages link to this image:
+</td><td>
+{{int:Linkstoimage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Linktrail&action=edit linktrail]<br>
+[[MediaWiki_talk:Linktrail|Talk]]
+</td><td>
+/^(&#91;a-z]+)(.*)$/sD
+</td><td>
+{{int:Linktrail}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Listform&action=edit listform]<br>
+[[MediaWiki_talk:Listform|Talk]]
+</td><td>
+list
+</td><td>
+{{int:Listform}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Listusers&action=edit listusers]<br>
+[[MediaWiki_talk:Listusers|Talk]]
+</td><td>
+User list
+</td><td>
+{{int:Listusers}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loadhist&action=edit loadhist]<br>
+[[MediaWiki_talk:Loadhist|Talk]]
+</td><td>
+Loading page history
+</td><td>
+{{int:Loadhist}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loadingrev&action=edit loadingrev]<br>
+[[MediaWiki_talk:Loadingrev|Talk]]
+</td><td>
+loading revision for diff
+</td><td>
+{{int:Loadingrev}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Localtime&action=edit localtime]<br>
+[[MediaWiki_talk:Localtime|Talk]]
+</td><td>
+Local time display
+</td><td>
+{{int:Localtime}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lockbtn&action=edit lockbtn]<br>
+[[MediaWiki_talk:Lockbtn|Talk]]
+</td><td>
+Lock database
+</td><td>
+{{int:Lockbtn}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lockconfirm&action=edit lockconfirm]<br>
+[[MediaWiki_talk:Lockconfirm|Talk]]
+</td><td>
+Yes, I really want to lock the database.
+</td><td>
+{{int:Lockconfirm}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lockdb&action=edit lockdb]<br>
+[[MediaWiki_talk:Lockdb|Talk]]
+</td><td>
+Lock database
+</td><td>
+{{int:Lockdb}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lockdbsuccesssub&action=edit lockdbsuccesssub]<br>
+[[MediaWiki_talk:Lockdbsuccesssub|Talk]]
+</td><td>
+Database lock succeeded
+</td><td>
+{{int:Lockdbsuccesssub}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lockdbsuccesstext&action=edit lockdbsuccesstext]<br>
+[[MediaWiki_talk:Lockdbsuccesstext|Talk]]
+</td><td>
+The database has been locked.
+&lt;br /&gt;Remember to remove the lock after your maintenance is complete.
+</td><td>
+{{int:Lockdbsuccesstext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lockdbtext&action=edit lockdbtext]<br>
+[[MediaWiki_talk:Lockdbtext|Talk]]
+</td><td>
+Locking the database will suspend the ability of all
+users to edit pages, change their preferences, edit their watchlists, and
+other things requiring changes in the database.
+Please confirm that this is what you intend to do, and that you will
+unlock the database when your maintenance is done.
+</td><td>
+{{int:Lockdbtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Locknoconfirm&action=edit locknoconfirm]<br>
+[[MediaWiki_talk:Locknoconfirm|Talk]]
+</td><td>
+You did not check the confirmation box.
+</td><td>
+{{int:Locknoconfirm}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Login&action=edit login]<br>
+[[MediaWiki_talk:Login|Talk]]
+</td><td>
+Log in
+</td><td>
+{{int:Login}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginend&action=edit loginend]<br>
+[[MediaWiki_talk:Loginend|Talk]]
+</td><td>
+&amp;nbsp;
+</td><td>
+{{int:Loginend}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginerror&action=edit loginerror]<br>
+[[MediaWiki_talk:Loginerror|Talk]]
+</td><td>
+Login error
+</td><td>
+{{int:Loginerror}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginpagetitle&action=edit loginpagetitle]<br>
+[[MediaWiki_talk:Loginpagetitle|Talk]]
+</td><td>
+User login
+</td><td>
+{{int:Loginpagetitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginproblem&action=edit loginproblem]<br>
+[[MediaWiki_talk:Loginproblem|Talk]]
+</td><td>
+&lt;b&gt;There has been a problem with your login.&lt;/b&gt;&lt;br /&gt;Try again!
+</td><td>
+{{int:Loginproblem}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginprompt&action=edit loginprompt]<br>
+[[MediaWiki_talk:Loginprompt|Talk]]
+</td><td>
+You must have cookies enabled to log in to Wiktionary.
+</td><td>
+{{int:Loginprompt}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginreqtext&action=edit loginreqtext]<br>
+[[MediaWiki_talk:Loginreqtext|Talk]]
+</td><td>
+You must &#91;&#91;special:Userlogin&#124;login]] to view other pages.
+</td><td>
+{{int:Loginreqtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginreqtitle&action=edit loginreqtitle]<br>
+[[MediaWiki_talk:Loginreqtitle|Talk]]
+</td><td>
+Login Required
+</td><td>
+{{int:Loginreqtitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginsuccess&action=edit loginsuccess]<br>
+[[MediaWiki_talk:Loginsuccess|Talk]]
+</td><td>
+You are now logged in to Wiktionary as &quot;$1&quot;.
+</td><td>
+{{int:Loginsuccess}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Loginsuccesstitle&action=edit loginsuccesstitle]<br>
+[[MediaWiki_talk:Loginsuccesstitle|Talk]]
+</td><td>
+Login successful
+</td><td>
+{{int:Loginsuccesstitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Logout&action=edit logout]<br>
+[[MediaWiki_talk:Logout|Talk]]
+</td><td>
+Log out
+</td><td>
+{{int:Logout}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Logouttext&action=edit logouttext]<br>
+[[MediaWiki_talk:Logouttext|Talk]]
+</td><td>
+You are now logged out.
+You can continue to use Wiktionary anonymously, or you can log in
+again as the same or as a different user. Note that some pages may
+continue to be displayed as if you were still logged in, until you clear
+your browser cache
+
+</td><td>
+{{int:Logouttext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Logouttitle&action=edit logouttitle]<br>
+[[MediaWiki_talk:Logouttitle|Talk]]
+</td><td>
+User logout
+</td><td>
+{{int:Logouttitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Lonelypages&action=edit lonelypages]<br>
+[[MediaWiki_talk:Lonelypages|Talk]]
+</td><td>
+Orphaned pages
+</td><td>
+{{int:Lonelypages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Longpages&action=edit longpages]<br>
+[[MediaWiki_talk:Longpages|Talk]]
+</td><td>
+Long pages
+</td><td>
+{{int:Longpages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Longpagewarning&action=edit longpagewarning]<br>
+[[MediaWiki_talk:Longpagewarning|Talk]]
+</td><td>
+WARNING: This page is $1 kilobytes long; some
+browsers may have problems editing pages approaching or longer than 32kb.
+Please consider breaking the page into smaller sections.
+</td><td>
+{{int:Longpagewarning}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mailerror&action=edit mailerror]<br>
+[[MediaWiki_talk:Mailerror|Talk]]
+</td><td>
+Error sending mail: $1
+</td><td>
+{{int:Mailerror}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mailmypassword&action=edit mailmypassword]<br>
+[[MediaWiki_talk:Mailmypassword|Talk]]
+</td><td>
+Mail me a new password
+</td><td>
+{{int:Mailmypassword}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mailnologin&action=edit mailnologin]<br>
+[[MediaWiki_talk:Mailnologin|Talk]]
+</td><td>
+No send address
+</td><td>
+{{int:Mailnologin}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mailnologintext&action=edit mailnologintext]<br>
+[[MediaWiki_talk:Mailnologintext|Talk]]
+</td><td>
+You must be &lt;a href=&quot;{{localurl:Special:Userlogin&quot;&gt;logged in&lt;/a&gt;
+and have a valid e-mail address in your &lt;a href=&quot;/wiki/Special:Preferences&quot;&gt;preferences&lt;/a&gt;
+to send e-mail to other users.
+</td><td>
+{{int:Mailnologintext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mainpage&action=edit mainpage]<br>
+[[MediaWiki_talk:Mainpage|Talk]]
+</td><td>
+Main Page
+</td><td>
+{{int:Mainpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mainpagedocfooter&action=edit mainpagedocfooter]<br>
+[[MediaWiki_talk:Mainpagedocfooter|Talk]]
+</td><td>
+Please see &#91;http&#58;//meta.wikipedia.org/wiki/MediaWiki_i18n documentation on customizing the interface]
+and the &#91;http&#58;//meta.wikipedia.org/wiki/MediaWiki_User%27s_Guide User&#39;s Guide] for usage and configuration help.
+</td><td>
+{{int:Mainpagedocfooter}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mainpagetext&action=edit mainpagetext]<br>
+[[MediaWiki_talk:Mainpagetext|Talk]]
+</td><td>
+Wiki software successfully installed.
+</td><td>
+{{int:Mainpagetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Maintenance&action=edit maintenance]<br>
+[[MediaWiki_talk:Maintenance|Talk]]
+</td><td>
+Maintenance page
+</td><td>
+{{int:Maintenance}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Maintenancebacklink&action=edit maintenancebacklink]<br>
+[[MediaWiki_talk:Maintenancebacklink|Talk]]
+</td><td>
+Back to Maintenance Page
+</td><td>
+{{int:Maintenancebacklink}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Maintnancepagetext&action=edit maintnancepagetext]<br>
+[[MediaWiki_talk:Maintnancepagetext|Talk]]
+</td><td>
+This page includes several handy tools for everyday maintenance. Some of these functions tend to stress the database, so please do not hit reload after every item you fixed ;-)
+</td><td>
+{{int:Maintnancepagetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysop&action=edit makesysop]<br>
+[[MediaWiki_talk:Makesysop|Talk]]
+</td><td>
+Make a user into a sysop
+</td><td>
+{{int:Makesysop}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysopfail&action=edit makesysopfail]<br>
+[[MediaWiki_talk:Makesysopfail|Talk]]
+</td><td>
+&lt;b&gt;User &quot;$1&quot; could not be made into a sysop. (Did you enter the name correctly?)&lt;/b&gt;
+</td><td>
+{{int:Makesysopfail}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysopname&action=edit makesysopname]<br>
+[[MediaWiki_talk:Makesysopname|Talk]]
+</td><td>
+Name of the user:
+</td><td>
+{{int:Makesysopname}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysopok&action=edit makesysopok]<br>
+[[MediaWiki_talk:Makesysopok|Talk]]
+</td><td>
+&lt;b&gt;User &quot;$1&quot; is now a sysop&lt;/b&gt;
+</td><td>
+{{int:Makesysopok}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysopsubmit&action=edit makesysopsubmit]<br>
+[[MediaWiki_talk:Makesysopsubmit|Talk]]
+</td><td>
+Make this user into a sysop
+</td><td>
+{{int:Makesysopsubmit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysoptext&action=edit makesysoptext]<br>
+[[MediaWiki_talk:Makesysoptext|Talk]]
+</td><td>
+This form is used by bureaucrats to turn ordinary users into administrators.
+Type the name of the user in the box and press the button to make the user an administrator
+</td><td>
+{{int:Makesysoptext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Makesysoptitle&action=edit makesysoptitle]<br>
+[[MediaWiki_talk:Makesysoptitle|Talk]]
+</td><td>
+Make a user into a sysop
+</td><td>
+{{int:Makesysoptitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Matchtotals&action=edit matchtotals]<br>
+[[MediaWiki_talk:Matchtotals|Talk]]
+</td><td>
+The query &quot;$1&quot; matched $2 page titles
+and the text of $3 pages.
+</td><td>
+{{int:Matchtotals}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math&action=edit math]<br>
+[[MediaWiki_talk:Math|Talk]]
+</td><td>
+Rendering math
+</td><td>
+{{int:Math}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_bad_output&action=edit math_bad_output]<br>
+[[MediaWiki_talk:Math_bad_output|Talk]]
+</td><td>
+Can&#39;t write to or create math output directory
+</td><td>
+{{int:Math_bad_output}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_bad_tmpdir&action=edit math_bad_tmpdir]<br>
+[[MediaWiki_talk:Math_bad_tmpdir|Talk]]
+</td><td>
+Can&#39;t write to or create math temp directory
+</td><td>
+{{int:Math_bad_tmpdir}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_failure&action=edit math_failure]<br>
+[[MediaWiki_talk:Math_failure|Talk]]
+</td><td>
+Failed to parse
+</td><td>
+{{int:Math_failure}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_image_error&action=edit math_image_error]<br>
+[[MediaWiki_talk:Math_image_error|Talk]]
+</td><td>
+PNG conversion failed; check for correct installation of latex, dvips, gs, and convert
+</td><td>
+{{int:Math_image_error}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_lexing_error&action=edit math_lexing_error]<br>
+[[MediaWiki_talk:Math_lexing_error|Talk]]
+</td><td>
+lexing error
+</td><td>
+{{int:Math_lexing_error}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_notexvc&action=edit math_notexvc]<br>
+[[MediaWiki_talk:Math_notexvc|Talk]]
+</td><td>
+Missing texvc executable; please see math/README to configure.
+</td><td>
+{{int:Math_notexvc}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_sample&action=edit math_sample]<br>
+[[MediaWiki_talk:Math_sample|Talk]]
+</td><td>
+Insert formula here
+</td><td>
+{{int:Math_sample}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_syntax_error&action=edit math_syntax_error]<br>
+[[MediaWiki_talk:Math_syntax_error|Talk]]
+</td><td>
+syntax error
+</td><td>
+{{int:Math_syntax_error}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_tip&action=edit math_tip]<br>
+[[MediaWiki_talk:Math_tip|Talk]]
+</td><td>
+Mathematical formula (LaTeX)
+</td><td>
+{{int:Math_tip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_unknown_error&action=edit math_unknown_error]<br>
+[[MediaWiki_talk:Math_unknown_error|Talk]]
+</td><td>
+unknown error
+</td><td>
+{{int:Math_unknown_error}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Math_unknown_function&action=edit math_unknown_function]<br>
+[[MediaWiki_talk:Math_unknown_function|Talk]]
+</td><td>
+unknown function
+</td><td>
+{{int:Math_unknown_function}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Media_sample&action=edit media_sample]<br>
+[[MediaWiki_talk:Media_sample|Talk]]
+</td><td>
+Example.mp3
+</td><td>
+{{int:Media_sample}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Media_tip&action=edit media_tip]<br>
+[[MediaWiki_talk:Media_tip|Talk]]
+</td><td>
+Media file link
+</td><td>
+{{int:Media_tip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Minlength&action=edit minlength]<br>
+[[MediaWiki_talk:Minlength|Talk]]
+</td><td>
+Image names must be at least three letters.
+</td><td>
+{{int:Minlength}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Minoredit&action=edit minoredit]<br>
+[[MediaWiki_talk:Minoredit|Talk]]
+</td><td>
+This is a minor edit
+</td><td>
+{{int:Minoredit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Minoreditletter&action=edit minoreditletter]<br>
+[[MediaWiki_talk:Minoreditletter|Talk]]
+</td><td>
+M
+</td><td>
+{{int:Minoreditletter}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mispeelings&action=edit mispeelings]<br>
+[[MediaWiki_talk:Mispeelings|Talk]]
+</td><td>
+Pages with misspellings
+</td><td>
+{{int:Mispeelings}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mispeelingspage&action=edit mispeelingspage]<br>
+[[MediaWiki_talk:Mispeelingspage|Talk]]
+</td><td>
+List of common misspellings
+</td><td>
+{{int:Mispeelingspage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mispeelingstext&action=edit mispeelingstext]<br>
+[[MediaWiki_talk:Mispeelingstext|Talk]]
+</td><td>
+The following pages contain a common misspelling, which are listed on $1. The correct spelling might be given (like this).
+</td><td>
+{{int:Mispeelingstext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Missingarticle&action=edit missingarticle]<br>
+[[MediaWiki_talk:Missingarticle|Talk]]
+</td><td>
+The database did not find the text of a page
+that it should have found, named &quot;$1&quot;.
+
+&lt;p&gt;This is usually caused by following an outdated diff or history link to a
+page that has been deleted.
+
+&lt;p&gt;If this is not the case, you may have found a bug in the software.
+Please report this to an administrator, making note of the URL.
+</td><td>
+{{int:Missingarticle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Missingimage&action=edit missingimage]<br>
+[[MediaWiki_talk:Missingimage|Talk]]
+</td><td>
+&lt;b&gt;Missing image&lt;/b&gt;&lt;br /&gt;&lt;i&gt;$1&lt;/i&gt;
+
+</td><td>
+{{int:Missingimage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Missinglanguagelinks&action=edit missinglanguagelinks]<br>
+[[MediaWiki_talk:Missinglanguagelinks|Talk]]
+</td><td>
+Missing Language Links
+</td><td>
+{{int:Missinglanguagelinks}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Missinglanguagelinksbutton&action=edit missinglanguagelinksbutton]<br>
+[[MediaWiki_talk:Missinglanguagelinksbutton|Talk]]
+</td><td>
+Find missing language links for
+</td><td>
+{{int:Missinglanguagelinksbutton}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Missinglanguagelinkstext&action=edit missinglanguagelinkstext]<br>
+[[MediaWiki_talk:Missinglanguagelinkstext|Talk]]
+</td><td>
+These pages do &lt;i&gt;not&lt;/i&gt; link to their counterpart in $1. Redirects and subpages are &lt;i&gt;not&lt;/i&gt; shown.
+</td><td>
+{{int:Missinglanguagelinkstext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Moredotdotdot&action=edit moredotdotdot]<br>
+[[MediaWiki_talk:Moredotdotdot|Talk]]
+</td><td>
+More...
+</td><td>
+{{int:Moredotdotdot}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Move&action=edit move]<br>
+[[MediaWiki_talk:Move|Talk]]
+</td><td>
+Move
+</td><td>
+{{int:Move}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movearticle&action=edit movearticle]<br>
+[[MediaWiki_talk:Movearticle|Talk]]
+</td><td>
+Move page
+</td><td>
+{{int:Movearticle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movedto&action=edit movedto]<br>
+[[MediaWiki_talk:Movedto|Talk]]
+</td><td>
+moved to
+</td><td>
+{{int:Movedto}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movenologin&action=edit movenologin]<br>
+[[MediaWiki_talk:Movenologin|Talk]]
+</td><td>
+Not logged in
+</td><td>
+{{int:Movenologin}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movenologintext&action=edit movenologintext]<br>
+[[MediaWiki_talk:Movenologintext|Talk]]
+</td><td>
+You must be a registered user and &lt;a href=&quot;/wiki/Special:Userlogin&quot;&gt;logged in&lt;/a&gt;
+to move a page.
+</td><td>
+{{int:Movenologintext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movepage&action=edit movepage]<br>
+[[MediaWiki_talk:Movepage|Talk]]
+</td><td>
+Move page
+</td><td>
+{{int:Movepage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movepagebtn&action=edit movepagebtn]<br>
+[[MediaWiki_talk:Movepagebtn|Talk]]
+</td><td>
+Move page
+</td><td>
+{{int:Movepagebtn}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movepagetalktext&action=edit movepagetalktext]<br>
+[[MediaWiki_talk:Movepagetalktext|Talk]]
+</td><td>
+The associated talk page, if any, will be automatically moved along with it &#39;&#39;&#39;unless:&#39;&#39;&#39;
+*You are moving the page across namespaces,
+*A non-empty talk page already exists under the new name, or
+*You uncheck the box below.
+
+In those cases, you will have to move or merge the page manually if desired.
+</td><td>
+{{int:Movepagetalktext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movepagetext&action=edit movepagetext]<br>
+[[MediaWiki_talk:Movepagetext|Talk]]
+</td><td>
+Using the form below will rename a page, moving all
+of its history to the new name.
+The old title will become a redirect page to the new title.
+Links to the old page title will not be changed; be sure to
+&#91;&#91;Special:Maintenance&#124;check]] for double or broken redirects.
+You are responsible for making sure that links continue to
+point where they are supposed to go.
+
+Note that the page will &#39;&#39;&#39;not&#39;&#39;&#39; be moved if there is already
+a page at the new title, unless it is empty or a redirect and has no
+past edit history. This means that you can rename a page back to where
+it was just renamed from if you make a mistake, and you cannot overwrite
+an existing page.
+
+&lt;b&gt;WARNING!&lt;/b&gt;
+This can be a drastic and unexpected change for a popular page;
+please be sure you understand the consequences of this before
+proceeding.
+</td><td>
+{{int:Movepagetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movetalk&action=edit movetalk]<br>
+[[MediaWiki_talk:Movetalk|Talk]]
+</td><td>
+Move &quot;talk&quot; page as well, if applicable.
+</td><td>
+{{int:Movetalk}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Movethispage&action=edit movethispage]<br>
+[[MediaWiki_talk:Movethispage|Talk]]
+</td><td>
+Move this page
+</td><td>
+{{int:Movethispage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mycontris&action=edit mycontris]<br>
+[[MediaWiki_talk:Mycontris|Talk]]
+</td><td>
+My contributions
+</td><td>
+{{int:Mycontris}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mypage&action=edit mypage]<br>
+[[MediaWiki_talk:Mypage|Talk]]
+</td><td>
+My page
+</td><td>
+{{int:Mypage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Mytalk&action=edit mytalk]<br>
+[[MediaWiki_talk:Mytalk|Talk]]
+</td><td>
+My talk
+</td><td>
+{{int:Mytalk}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Navigation&action=edit navigation]<br>
+[[MediaWiki_talk:Navigation|Talk]]
+</td><td>
+Navigation
+</td><td>
+{{int:Navigation}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nbytes&action=edit nbytes]<br>
+[[MediaWiki_talk:Nbytes|Talk]]
+</td><td>
+$1 bytes
+</td><td>
+{{int:Nbytes}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nchanges&action=edit nchanges]<br>
+[[MediaWiki_talk:Nchanges|Talk]]
+</td><td>
+$1 changes
+</td><td>
+{{int:Nchanges}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newarticle&action=edit newarticle]<br>
+[[MediaWiki_talk:Newarticle|Talk]]
+</td><td>
+(New)
+</td><td>
+{{int:Newarticle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newarticletext&action=edit newarticletext]<br>
+[[MediaWiki_talk:Newarticletext|Talk]]
+</td><td>
+You&#39;ve followed a link to a page that doesn&#39;t exist yet.
+To create the page, start typing in the box below
+(see the &#91;&#91;Wiktionary:Help&#124;help page]] for more info).
+If you are here by mistake, just click your browser&#39;s &#39;&#39;&#39;back&#39;&#39;&#39; button.
+</td><td>
+{{int:Newarticletext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newmessages&action=edit newmessages]<br>
+[[MediaWiki_talk:Newmessages|Talk]]
+</td><td>
+You have $1.
+</td><td>
+{{int:Newmessages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newmessageslink&action=edit newmessageslink]<br>
+[[MediaWiki_talk:Newmessageslink|Talk]]
+</td><td>
+new messages
+</td><td>
+{{int:Newmessageslink}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newpage&action=edit newpage]<br>
+[[MediaWiki_talk:Newpage|Talk]]
+</td><td>
+New page
+</td><td>
+{{int:Newpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newpageletter&action=edit newpageletter]<br>
+[[MediaWiki_talk:Newpageletter|Talk]]
+</td><td>
+N
+</td><td>
+{{int:Newpageletter}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newpages&action=edit newpages]<br>
+[[MediaWiki_talk:Newpages|Talk]]
+</td><td>
+New pages
+</td><td>
+{{int:Newpages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newpassword&action=edit newpassword]<br>
+[[MediaWiki_talk:Newpassword|Talk]]
+</td><td>
+New password
+</td><td>
+{{int:Newpassword}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newtitle&action=edit newtitle]<br>
+[[MediaWiki_talk:Newtitle|Talk]]
+</td><td>
+To new title
+</td><td>
+{{int:Newtitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Newusersonly&action=edit newusersonly]<br>
+[[MediaWiki_talk:Newusersonly|Talk]]
+</td><td>
+ (new users only)
+</td><td>
+{{int:Newusersonly}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Next&action=edit next]<br>
+[[MediaWiki_talk:Next|Talk]]
+</td><td>
+next
+</td><td>
+{{int:Next}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nextn&action=edit nextn]<br>
+[[MediaWiki_talk:Nextn|Talk]]
+</td><td>
+next $1
+</td><td>
+{{int:Nextn}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nlinks&action=edit nlinks]<br>
+[[MediaWiki_talk:Nlinks|Talk]]
+</td><td>
+$1 links
+</td><td>
+{{int:Nlinks}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noaffirmation&action=edit noaffirmation]<br>
+[[MediaWiki_talk:Noaffirmation|Talk]]
+</td><td>
+You must affirm that your upload does not violate
+any copyrights.
+</td><td>
+{{int:Noaffirmation}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noarticletext&action=edit noarticletext]<br>
+[[MediaWiki_talk:Noarticletext|Talk]]
+</td><td>
+(There is currently no text in this page)
+</td><td>
+{{int:Noarticletext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noblockreason&action=edit noblockreason]<br>
+[[MediaWiki_talk:Noblockreason|Talk]]
+</td><td>
+You must supply a reason for the block.
+</td><td>
+{{int:Noblockreason}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noconnect&action=edit noconnect]<br>
+[[MediaWiki_talk:Noconnect|Talk]]
+</td><td>
+Sorry! The wiki is experiencing some technical difficulties, and cannot contact the database server.
+</td><td>
+{{int:Noconnect}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nocontribs&action=edit nocontribs]<br>
+[[MediaWiki_talk:Nocontribs|Talk]]
+</td><td>
+No changes were found matching these criteria.
+</td><td>
+{{int:Nocontribs}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nocookieslogin&action=edit nocookieslogin]<br>
+[[MediaWiki_talk:Nocookieslogin|Talk]]
+</td><td>
+Wiktionary uses cookies to log in users. You have cookies disabled. Please enable them and try again.
+</td><td>
+{{int:Nocookieslogin}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nocookiesnew&action=edit nocookiesnew]<br>
+[[MediaWiki_talk:Nocookiesnew|Talk]]
+</td><td>
+The user account was created, but you are not logged in. Wiktionary uses cookies to log in users. You have cookies disabled. Please enable them, then log in with your new username and password.
+</td><td>
+{{int:Nocookiesnew}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nocreativecommons&action=edit nocreativecommons]<br>
+[[MediaWiki_talk:Nocreativecommons|Talk]]
+</td><td>
+Creative Commons RDF metadata disabled for this server.
+</td><td>
+{{int:Nocreativecommons}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nodb&action=edit nodb]<br>
+[[MediaWiki_talk:Nodb|Talk]]
+</td><td>
+Could not select database $1
+</td><td>
+{{int:Nodb}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nodublincore&action=edit nodublincore]<br>
+[[MediaWiki_talk:Nodublincore|Talk]]
+</td><td>
+Dublin Core RDF metadata disabled for this server.
+</td><td>
+{{int:Nodublincore}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noemail&action=edit noemail]<br>
+[[MediaWiki_talk:Noemail|Talk]]
+</td><td>
+There is no e-mail address recorded for user &quot;$1&quot;.
+</td><td>
+{{int:Noemail}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noemailtext&action=edit noemailtext]<br>
+[[MediaWiki_talk:Noemailtext|Talk]]
+</td><td>
+This user has not specified a valid e-mail address,
+or has chosen not to receive e-mail from other users.
+</td><td>
+{{int:Noemailtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noemailtitle&action=edit noemailtitle]<br>
+[[MediaWiki_talk:Noemailtitle|Talk]]
+</td><td>
+No e-mail address
+</td><td>
+{{int:Noemailtitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nogomatch&action=edit nogomatch]<br>
+[[MediaWiki_talk:Nogomatch|Talk]]
+</td><td>
+No page with this exact title exists, trying full text search.
+</td><td>
+{{int:Nogomatch}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nohistory&action=edit nohistory]<br>
+[[MediaWiki_talk:Nohistory|Talk]]
+</td><td>
+There is no edit history for this page.
+</td><td>
+{{int:Nohistory}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nolinkshere&action=edit nolinkshere]<br>
+[[MediaWiki_talk:Nolinkshere|Talk]]
+</td><td>
+No pages link to here.
+</td><td>
+{{int:Nolinkshere}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nolinkstoimage&action=edit nolinkstoimage]<br>
+[[MediaWiki_talk:Nolinkstoimage|Talk]]
+</td><td>
+There are no pages that link to this image.
+</td><td>
+{{int:Nolinkstoimage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Noname&action=edit noname]<br>
+[[MediaWiki_talk:Noname|Talk]]
+</td><td>
+You have not specified a valid user name.
+</td><td>
+{{int:Noname}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nonefound&action=edit nonefound]<br>
+[[MediaWiki_talk:Nonefound|Talk]]
+</td><td>
+&lt;strong&gt;Note&lt;/strong&gt;: unsuccessful searches are
+often caused by searching for common words like &quot;have&quot; and &quot;from&quot;,
+which are not indexed, or by specifying more than one search term (only pages
+containing all of the search terms will appear in the result).
+</td><td>
+{{int:Nonefound}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nospecialpagetext&action=edit nospecialpagetext]<br>
+[[MediaWiki_talk:Nospecialpagetext|Talk]]
+</td><td>
+You have requested a special page that is not
+recognized by the wiki.
+</td><td>
+{{int:Nospecialpagetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nosuchaction&action=edit nosuchaction]<br>
+[[MediaWiki_talk:Nosuchaction|Talk]]
+</td><td>
+No such action
+</td><td>
+{{int:Nosuchaction}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nosuchactiontext&action=edit nosuchactiontext]<br>
+[[MediaWiki_talk:Nosuchactiontext|Talk]]
+</td><td>
+The action specified by the URL is not
+recognized by the wiki
+</td><td>
+{{int:Nosuchactiontext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nosuchspecialpage&action=edit nosuchspecialpage]<br>
+[[MediaWiki_talk:Nosuchspecialpage|Talk]]
+</td><td>
+No such special page
+</td><td>
+{{int:Nosuchspecialpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nosuchuser&action=edit nosuchuser]<br>
+[[MediaWiki_talk:Nosuchuser|Talk]]
+</td><td>
+There is no user by the name &quot;$1&quot;.
+Check your spelling, or use the form below to create a new user account.
+</td><td>
+{{int:Nosuchuser}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notacceptable&action=edit notacceptable]<br>
+[[MediaWiki_talk:Notacceptable|Talk]]
+</td><td>
+The wiki server can&#39;t provide data in a format your client can read.
+</td><td>
+{{int:Notacceptable}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notanarticle&action=edit notanarticle]<br>
+[[MediaWiki_talk:Notanarticle|Talk]]
+</td><td>
+Not a content page
+</td><td>
+{{int:Notanarticle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notargettext&action=edit notargettext]<br>
+[[MediaWiki_talk:Notargettext|Talk]]
+</td><td>
+You have not specified a target page or user
+to perform this function on.
+</td><td>
+{{int:Notargettext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notargettitle&action=edit notargettitle]<br>
+[[MediaWiki_talk:Notargettitle|Talk]]
+</td><td>
+No target
+</td><td>
+{{int:Notargettitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Note&action=edit note]<br>
+[[MediaWiki_talk:Note|Talk]]
+</td><td>
+&lt;strong&gt;Note:&lt;/strong&gt;
+</td><td>
+{{int:Note}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notextmatches&action=edit notextmatches]<br>
+[[MediaWiki_talk:Notextmatches|Talk]]
+</td><td>
+No page text matches
+</td><td>
+{{int:Notextmatches}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notitlematches&action=edit notitlematches]<br>
+[[MediaWiki_talk:Notitlematches|Talk]]
+</td><td>
+No page title matches
+</td><td>
+{{int:Notitlematches}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Notloggedin&action=edit notloggedin]<br>
+[[MediaWiki_talk:Notloggedin|Talk]]
+</td><td>
+Not logged in
+</td><td>
+{{int:Notloggedin}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nowatchlist&action=edit nowatchlist]<br>
+[[MediaWiki_talk:Nowatchlist|Talk]]
+</td><td>
+You have no items on your watchlist.
+</td><td>
+{{int:Nowatchlist}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nowiki_sample&action=edit nowiki_sample]<br>
+[[MediaWiki_talk:Nowiki_sample|Talk]]
+</td><td>
+Insert non-formatted text here
+</td><td>
+{{int:Nowiki_sample}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nowiki_tip&action=edit nowiki_tip]<br>
+[[MediaWiki_talk:Nowiki_tip|Talk]]
+</td><td>
+Ignore wiki formatting
+</td><td>
+{{int:Nowiki_tip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-category&action=edit nstab-category]<br>
+[[MediaWiki_talk:Nstab-category|Talk]]
+</td><td>
+Category
+</td><td>
+{{int:Nstab-category}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-help&action=edit nstab-help]<br>
+[[MediaWiki_talk:Nstab-help|Talk]]
+</td><td>
+Help
+</td><td>
+{{int:Nstab-help}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-image&action=edit nstab-image]<br>
+[[MediaWiki_talk:Nstab-image|Talk]]
+</td><td>
+Image
+</td><td>
+{{int:Nstab-image}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-main&action=edit nstab-main]<br>
+[[MediaWiki_talk:Nstab-main|Talk]]
+</td><td>
+Article
+</td><td>
+{{int:Nstab-main}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-media&action=edit nstab-media]<br>
+[[MediaWiki_talk:Nstab-media|Talk]]
+</td><td>
+Media
+</td><td>
+{{int:Nstab-media}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-mediawiki&action=edit nstab-mediawiki]<br>
+[[MediaWiki_talk:Nstab-mediawiki|Talk]]
+</td><td>
+Message
+</td><td>
+{{int:Nstab-mediawiki}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-special&action=edit nstab-special]<br>
+[[MediaWiki_talk:Nstab-special|Talk]]
+</td><td>
+Special
+</td><td>
+{{int:Nstab-special}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-template&action=edit nstab-template]<br>
+[[MediaWiki_talk:Nstab-template|Talk]]
+</td><td>
+Template
+</td><td>
+{{int:Nstab-template}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-user&action=edit nstab-user]<br>
+[[MediaWiki_talk:Nstab-user|Talk]]
+</td><td>
+User page
+</td><td>
+{{int:Nstab-user}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nstab-wp&action=edit nstab-wp]<br>
+[[MediaWiki_talk:Nstab-wp|Talk]]
+</td><td>
+About
+</td><td>
+{{int:Nstab-wp}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Nviews&action=edit nviews]<br>
+[[MediaWiki_talk:Nviews|Talk]]
+</td><td>
+$1 views
+</td><td>
+{{int:Nviews}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ok&action=edit ok]<br>
+[[MediaWiki_talk:Ok|Talk]]
+</td><td>
+OK
+</td><td>
+{{int:Ok}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Oldpassword&action=edit oldpassword]<br>
+[[MediaWiki_talk:Oldpassword|Talk]]
+</td><td>
+Old password
+</td><td>
+{{int:Oldpassword}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Orig&action=edit orig]<br>
+[[MediaWiki_talk:Orig|Talk]]
+</td><td>
+orig
+</td><td>
+{{int:Orig}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Orphans&action=edit orphans]<br>
+[[MediaWiki_talk:Orphans|Talk]]
+</td><td>
+Orphaned pages
+</td><td>
+{{int:Orphans}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Othercontribs&action=edit othercontribs]<br>
+[[MediaWiki_talk:Othercontribs|Talk]]
+</td><td>
+Based on work by $1.
+</td><td>
+{{int:Othercontribs}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Otherlanguages&action=edit otherlanguages]<br>
+[[MediaWiki_talk:Otherlanguages|Talk]]
+</td><td>
+Other languages
+</td><td>
+{{int:Otherlanguages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Pagemovedsub&action=edit pagemovedsub]<br>
+[[MediaWiki_talk:Pagemovedsub|Talk]]
+</td><td>
+Move succeeded
+</td><td>
+{{int:Pagemovedsub}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Pagemovedtext&action=edit pagemovedtext]<br>
+[[MediaWiki_talk:Pagemovedtext|Talk]]
+</td><td>
+Page &quot;&#91;&#91;$1]]&quot; moved to &quot;&#91;&#91;$2]]&quot;.
+</td><td>
+{{int:Pagemovedtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Pagetitle&action=edit pagetitle]<br>
+[[MediaWiki_talk:Pagetitle|Talk]]
+</td><td>
+$1 - Wiktionary
+</td><td>
+{{int:Pagetitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Passwordremindertext&action=edit passwordremindertext]<br>
+[[MediaWiki_talk:Passwordremindertext|Talk]]
+</td><td>
+Someone (probably you, from IP address $1)
+requested that we send you a new Wiktionary login password.
+The password for user &quot;$2&quot; is now &quot;$3&quot;.
+You should log in and change your password now.
+</td><td>
+{{int:Passwordremindertext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Passwordremindertitle&action=edit passwordremindertitle]<br>
+[[MediaWiki_talk:Passwordremindertitle|Talk]]
+</td><td>
+Password reminder from Wiktionary
+</td><td>
+{{int:Passwordremindertitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Passwordsent&action=edit passwordsent]<br>
+[[MediaWiki_talk:Passwordsent|Talk]]
+</td><td>
+A new password has been sent to the e-mail address
+registered for &quot;$1&quot;.
+Please log in again after you receive it.
+</td><td>
+{{int:Passwordsent}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Perfcached&action=edit perfcached]<br>
+[[MediaWiki_talk:Perfcached|Talk]]
+</td><td>
+The following data is cached and may not be completely up to date:
+</td><td>
+{{int:Perfcached}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Perfdisabled&action=edit perfdisabled]<br>
+[[MediaWiki_talk:Perfdisabled|Talk]]
+</td><td>
+Sorry! This feature has been temporarily disabled
+because it slows the database down to the point that no one can use
+the wiki.
+</td><td>
+{{int:Perfdisabled}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Perfdisabledsub&action=edit perfdisabledsub]<br>
+[[MediaWiki_talk:Perfdisabledsub|Talk]]
+</td><td>
+Here&#39;s a saved copy from $1:
+</td><td>
+{{int:Perfdisabledsub}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Personaltools&action=edit personaltools]<br>
+[[MediaWiki_talk:Personaltools|Talk]]
+</td><td>
+Personal tools
+</td><td>
+{{int:Personaltools}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Popularpages&action=edit popularpages]<br>
+[[MediaWiki_talk:Popularpages|Talk]]
+</td><td>
+Popular pages
+</td><td>
+{{int:Popularpages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Portal&action=edit portal]<br>
+[[MediaWiki_talk:Portal|Talk]]
+</td><td>
+Community portal
+</td><td>
+{{int:Portal}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Portal-url&action=edit portal-url]<br>
+[[MediaWiki_talk:Portal-url|Talk]]
+</td><td>
+Wiktionary:Community Portal
+</td><td>
+{{int:Portal-url}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Postcomment&action=edit postcomment]<br>
+[[MediaWiki_talk:Postcomment|Talk]]
+</td><td>
+Post a comment
+</td><td>
+{{int:Postcomment}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Poweredby&action=edit poweredby]<br>
+[[MediaWiki_talk:Poweredby|Talk]]
+</td><td>
+Wiktionary is powered by &#91;http&#58;//www.mediawiki.org/ MediaWiki], an open source wiki engine.
+</td><td>
+{{int:Poweredby}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Powersearch&action=edit powersearch]<br>
+[[MediaWiki_talk:Powersearch|Talk]]
+</td><td>
+Search
+</td><td>
+{{int:Powersearch}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Powersearchtext&action=edit powersearchtext]<br>
+[[MediaWiki_talk:Powersearchtext|Talk]]
+</td><td>
+
+Search in namespaces :&lt;br /&gt;
+$1&lt;br /&gt;
+$2 List redirects &amp;nbsp; Search for $3 $9
+</td><td>
+{{int:Powersearchtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Preferences&action=edit preferences]<br>
+[[MediaWiki_talk:Preferences|Talk]]
+</td><td>
+Preferences
+</td><td>
+{{int:Preferences}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefs-help-userdata&action=edit prefs-help-userdata]<br>
+[[MediaWiki_talk:Prefs-help-userdata|Talk]]
+</td><td>
+* &lt;strong&gt;Real name&lt;/strong&gt; (optional): if you choose to provide it this will be used for giving you attribution for your work.&lt;br/&gt;
+* &lt;strong&gt;Email&lt;/strong&gt; (optional): Enables people to contact you through the website without you having to reveal your
+email address to them, and it can be used to send you a new password if you forget it.
+</td><td>
+{{int:Prefs-help-userdata}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefs-misc&action=edit prefs-misc]<br>
+[[MediaWiki_talk:Prefs-misc|Talk]]
+</td><td>
+Misc settings
+</td><td>
+{{int:Prefs-misc}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefs-personal&action=edit prefs-personal]<br>
+[[MediaWiki_talk:Prefs-personal|Talk]]
+</td><td>
+User data
+</td><td>
+{{int:Prefs-personal}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefs-rc&action=edit prefs-rc]<br>
+[[MediaWiki_talk:Prefs-rc|Talk]]
+</td><td>
+Recent changes and stub display
+</td><td>
+{{int:Prefs-rc}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefslogintext&action=edit prefslogintext]<br>
+[[MediaWiki_talk:Prefslogintext|Talk]]
+</td><td>
+You are logged in as &quot;$1&quot;.
+Your internal ID number is $2.
+
+See &#91;&#91;Wiktionary:User preferences help]] for help deciphering the options.
+</td><td>
+{{int:Prefslogintext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefsnologin&action=edit prefsnologin]<br>
+[[MediaWiki_talk:Prefsnologin|Talk]]
+</td><td>
+Not logged in
+</td><td>
+{{int:Prefsnologin}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefsnologintext&action=edit prefsnologintext]<br>
+[[MediaWiki_talk:Prefsnologintext|Talk]]
+</td><td>
+You must be &lt;a href=&quot;/wiki/Special:Userlogin&quot;&gt;logged in&lt;/a&gt;
+to set user preferences.
+</td><td>
+{{int:Prefsnologintext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prefsreset&action=edit prefsreset]<br>
+[[MediaWiki_talk:Prefsreset|Talk]]
+</td><td>
+Preferences have been reset from storage.
+</td><td>
+{{int:Prefsreset}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Preview&action=edit preview]<br>
+[[MediaWiki_talk:Preview|Talk]]
+</td><td>
+Preview
+</td><td>
+{{int:Preview}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Previewconflict&action=edit previewconflict]<br>
+[[MediaWiki_talk:Previewconflict|Talk]]
+</td><td>
+This preview reflects the text in the upper
+text editing area as it will appear if you choose to save.
+</td><td>
+{{int:Previewconflict}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Previewnote&action=edit previewnote]<br>
+[[MediaWiki_talk:Previewnote|Talk]]
+</td><td>
+Remember that this is only a preview, and has not yet been saved!
+</td><td>
+{{int:Previewnote}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Prevn&action=edit prevn]<br>
+[[MediaWiki_talk:Prevn|Talk]]
+</td><td>
+previous $1
+</td><td>
+{{int:Prevn}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Printableversion&action=edit printableversion]<br>
+[[MediaWiki_talk:Printableversion|Talk]]
+</td><td>
+Printable version
+</td><td>
+{{int:Printableversion}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Printsubtitle&action=edit printsubtitle]<br>
+[[MediaWiki_talk:Printsubtitle|Talk]]
+</td><td>
+(From http&#58;//tl.wiktionary.org)
+</td><td>
+{{int:Printsubtitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protect&action=edit protect]<br>
+[[MediaWiki_talk:Protect|Talk]]
+</td><td>
+Protect
+</td><td>
+{{int:Protect}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectcomment&action=edit protectcomment]<br>
+[[MediaWiki_talk:Protectcomment|Talk]]
+</td><td>
+Reason for protecting
+</td><td>
+{{int:Protectcomment}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectedarticle&action=edit protectedarticle]<br>
+[[MediaWiki_talk:Protectedarticle|Talk]]
+</td><td>
+protected &#91;&#91;$1]]
+</td><td>
+{{int:Protectedarticle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectedpage&action=edit protectedpage]<br>
+[[MediaWiki_talk:Protectedpage|Talk]]
+</td><td>
+Protected page
+</td><td>
+{{int:Protectedpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectedpagewarning&action=edit protectedpagewarning]<br>
+[[MediaWiki_talk:Protectedpagewarning|Talk]]
+</td><td>
+WARNING: This page has been locked so that only
+users with sysop privileges can edit it. Be sure you are following the
+&lt;a href=&#39;/w/wiki.phtml/Wiktionary:Protected_page_guidelines&#39;&gt;protected page
+guidelines&lt;/a&gt;.
+</td><td>
+{{int:Protectedpagewarning}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectedtext&action=edit protectedtext]<br>
+[[MediaWiki_talk:Protectedtext|Talk]]
+</td><td>
+This page has been locked to prevent editing; there are
+a number of reasons why this may be so, please see
+&#91;&#91;Wiktionary:Protected page]].
+
+You can view and copy the source of this page:
+</td><td>
+{{int:Protectedtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectlogpage&action=edit protectlogpage]<br>
+[[MediaWiki_talk:Protectlogpage|Talk]]
+</td><td>
+Protection_log
+</td><td>
+{{int:Protectlogpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectlogtext&action=edit protectlogtext]<br>
+[[MediaWiki_talk:Protectlogtext|Talk]]
+</td><td>
+Below is a list of page locks/unlocks.
+See &#91;&#91;Wiktionary:Protected page]] for more information.
+</td><td>
+{{int:Protectlogtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectpage&action=edit protectpage]<br>
+[[MediaWiki_talk:Protectpage|Talk]]
+</td><td>
+Protect page
+</td><td>
+{{int:Protectpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectreason&action=edit protectreason]<br>
+[[MediaWiki_talk:Protectreason|Talk]]
+</td><td>
+(give a reason)
+</td><td>
+{{int:Protectreason}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectsub&action=edit protectsub]<br>
+[[MediaWiki_talk:Protectsub|Talk]]
+</td><td>
+(Protecting &quot;$1&quot;)
+</td><td>
+{{int:Protectsub}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Protectthispage&action=edit protectthispage]<br>
+[[MediaWiki_talk:Protectthispage|Talk]]
+</td><td>
+Protect this page
+</td><td>
+{{int:Protectthispage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Proxyblocker&action=edit proxyblocker]<br>
+[[MediaWiki_talk:Proxyblocker|Talk]]
+</td><td>
+Proxy blocker
+</td><td>
+{{int:Proxyblocker}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Proxyblockreason&action=edit proxyblockreason]<br>
+[[MediaWiki_talk:Proxyblockreason|Talk]]
+</td><td>
+Your IP address has been blocked because it is an open proxy. Please contact your Internet service provider or tech support and inform them of this serious security problem.
+</td><td>
+{{int:Proxyblockreason}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Proxyblocksuccess&action=edit proxyblocksuccess]<br>
+[[MediaWiki_talk:Proxyblocksuccess|Talk]]
+</td><td>
+Done.
+
+</td><td>
+{{int:Proxyblocksuccess}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbbrowse&action=edit qbbrowse]<br>
+[[MediaWiki_talk:Qbbrowse|Talk]]
+</td><td>
+Browse
+</td><td>
+{{int:Qbbrowse}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbedit&action=edit qbedit]<br>
+[[MediaWiki_talk:Qbedit|Talk]]
+</td><td>
+Edit
+</td><td>
+{{int:Qbedit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbfind&action=edit qbfind]<br>
+[[MediaWiki_talk:Qbfind|Talk]]
+</td><td>
+Find
+</td><td>
+{{int:Qbfind}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbmyoptions&action=edit qbmyoptions]<br>
+[[MediaWiki_talk:Qbmyoptions|Talk]]
+</td><td>
+My pages
+</td><td>
+{{int:Qbmyoptions}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbpageinfo&action=edit qbpageinfo]<br>
+[[MediaWiki_talk:Qbpageinfo|Talk]]
+</td><td>
+Context
+</td><td>
+{{int:Qbpageinfo}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbpageoptions&action=edit qbpageoptions]<br>
+[[MediaWiki_talk:Qbpageoptions|Talk]]
+</td><td>
+This page
+</td><td>
+{{int:Qbpageoptions}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbsettings&action=edit qbsettings]<br>
+[[MediaWiki_talk:Qbsettings|Talk]]
+</td><td>
+Quickbar settings
+</td><td>
+{{int:Qbsettings}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Qbspecialpages&action=edit qbspecialpages]<br>
+[[MediaWiki_talk:Qbspecialpages|Talk]]
+</td><td>
+Special pages
+</td><td>
+{{int:Qbspecialpages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Querybtn&action=edit querybtn]<br>
+[[MediaWiki_talk:Querybtn|Talk]]
+</td><td>
+Submit query
+</td><td>
+{{int:Querybtn}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Querysuccessful&action=edit querysuccessful]<br>
+[[MediaWiki_talk:Querysuccessful|Talk]]
+</td><td>
+Query successful
+</td><td>
+{{int:Querysuccessful}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Randompage&action=edit randompage]<br>
+[[MediaWiki_talk:Randompage|Talk]]
+</td><td>
+Random page
+</td><td>
+{{int:Randompage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Range_block_disabled&action=edit range_block_disabled]<br>
+[[MediaWiki_talk:Range_block_disabled|Talk]]
+</td><td>
+The sysop ability to create range blocks is disabled.
+</td><td>
+{{int:Range_block_disabled}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rchide&action=edit rchide]<br>
+[[MediaWiki_talk:Rchide|Talk]]
+</td><td>
+in $4 form; $1 minor edits; $2 secondary namespaces; $3 multiple edits.
+</td><td>
+{{int:Rchide}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rclinks&action=edit rclinks]<br>
+[[MediaWiki_talk:Rclinks|Talk]]
+</td><td>
+Show last $1 changes in last $2 days&lt;br /&gt;$3
+</td><td>
+{{int:Rclinks}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rclistfrom&action=edit rclistfrom]<br>
+[[MediaWiki_talk:Rclistfrom|Talk]]
+</td><td>
+Show new changes starting from $1
+</td><td>
+{{int:Rclistfrom}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rcliu&action=edit rcliu]<br>
+[[MediaWiki_talk:Rcliu|Talk]]
+</td><td>
+; $1 edits from logged in users
+</td><td>
+{{int:Rcliu}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rcloaderr&action=edit rcloaderr]<br>
+[[MediaWiki_talk:Rcloaderr|Talk]]
+</td><td>
+Loading recent changes
+</td><td>
+{{int:Rcloaderr}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rclsub&action=edit rclsub]<br>
+[[MediaWiki_talk:Rclsub|Talk]]
+</td><td>
+(to pages linked from &quot;$1&quot;)
+</td><td>
+{{int:Rclsub}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rcnote&action=edit rcnote]<br>
+[[MediaWiki_talk:Rcnote|Talk]]
+</td><td>
+Below are the last &lt;strong&gt;$1&lt;/strong&gt; changes in last &lt;strong&gt;$2&lt;/strong&gt; days.
+</td><td>
+{{int:Rcnote}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rcnotefrom&action=edit rcnotefrom]<br>
+[[MediaWiki_talk:Rcnotefrom|Talk]]
+</td><td>
+Below are the changes since &lt;b&gt;$2&lt;/b&gt; (up to &lt;b&gt;$1&lt;/b&gt; shown).
+</td><td>
+{{int:Rcnotefrom}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Readonly&action=edit readonly]<br>
+[[MediaWiki_talk:Readonly|Talk]]
+</td><td>
+Database locked
+</td><td>
+{{int:Readonly}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Readonlytext&action=edit readonlytext]<br>
+[[MediaWiki_talk:Readonlytext|Talk]]
+</td><td>
+The database is currently locked to new
+entries and other modifications, probably for routine database maintenance,
+after which it will be back to normal.
+The administrator who locked it offered this explanation:
+&lt;p&gt;$1
+</td><td>
+{{int:Readonlytext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Readonlywarning&action=edit readonlywarning]<br>
+[[MediaWiki_talk:Readonlywarning|Talk]]
+</td><td>
+WARNING: The database has been locked for maintenance,
+so you will not be able to save your edits right now. You may wish to cut-n-paste
+the text into a text file and save it for later.
+</td><td>
+{{int:Readonlywarning}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Recentchanges&action=edit recentchanges]<br>
+[[MediaWiki_talk:Recentchanges|Talk]]
+</td><td>
+Recent changes
+</td><td>
+{{int:Recentchanges}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Recentchangescount&action=edit recentchangescount]<br>
+[[MediaWiki_talk:Recentchangescount|Talk]]
+</td><td>
+Number of titles in recent changes
+</td><td>
+{{int:Recentchangescount}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Recentchangeslinked&action=edit recentchangeslinked]<br>
+[[MediaWiki_talk:Recentchangeslinked|Talk]]
+</td><td>
+Related changes
+</td><td>
+{{int:Recentchangeslinked}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Recentchangestext&action=edit recentchangestext]<br>
+[[MediaWiki_talk:Recentchangestext|Talk]]
+</td><td>
+Track the most recent changes to the wiki on this page.
+</td><td>
+{{int:Recentchangestext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Redirectedfrom&action=edit redirectedfrom]<br>
+[[MediaWiki_talk:Redirectedfrom|Talk]]
+</td><td>
+(Redirected from $1)
+</td><td>
+{{int:Redirectedfrom}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Remembermypassword&action=edit remembermypassword]<br>
+[[MediaWiki_talk:Remembermypassword|Talk]]
+</td><td>
+Remember my password across sessions.
+</td><td>
+{{int:Remembermypassword}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Removechecked&action=edit removechecked]<br>
+[[MediaWiki_talk:Removechecked|Talk]]
+</td><td>
+Remove checked items from watchlist
+</td><td>
+{{int:Removechecked}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Removedwatch&action=edit removedwatch]<br>
+[[MediaWiki_talk:Removedwatch|Talk]]
+</td><td>
+Removed from watchlist
+</td><td>
+{{int:Removedwatch}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Removedwatchtext&action=edit removedwatchtext]<br>
+[[MediaWiki_talk:Removedwatchtext|Talk]]
+</td><td>
+The page &quot;$1&quot; has been removed from your watchlist.
+</td><td>
+{{int:Removedwatchtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Removingchecked&action=edit removingchecked]<br>
+[[MediaWiki_talk:Removingchecked|Talk]]
+</td><td>
+Removing requested items from watchlist...
+</td><td>
+{{int:Removingchecked}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Resetprefs&action=edit resetprefs]<br>
+[[MediaWiki_talk:Resetprefs|Talk]]
+</td><td>
+Reset preferences
+</td><td>
+{{int:Resetprefs}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Restorelink&action=edit restorelink]<br>
+[[MediaWiki_talk:Restorelink|Talk]]
+</td><td>
+$1 deleted edits
+</td><td>
+{{int:Restorelink}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Resultsperpage&action=edit resultsperpage]<br>
+[[MediaWiki_talk:Resultsperpage|Talk]]
+</td><td>
+Hits to show per page
+</td><td>
+{{int:Resultsperpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Retrievedfrom&action=edit retrievedfrom]<br>
+[[MediaWiki_talk:Retrievedfrom|Talk]]
+</td><td>
+Retrieved from &quot;$1&quot;
+</td><td>
+{{int:Retrievedfrom}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Returnto&action=edit returnto]<br>
+[[MediaWiki_talk:Returnto|Talk]]
+</td><td>
+Return to $1.
+</td><td>
+{{int:Returnto}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Retypenew&action=edit retypenew]<br>
+[[MediaWiki_talk:Retypenew|Talk]]
+</td><td>
+Retype new password
+</td><td>
+{{int:Retypenew}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Reupload&action=edit reupload]<br>
+[[MediaWiki_talk:Reupload|Talk]]
+</td><td>
+Re-upload
+</td><td>
+{{int:Reupload}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Reuploaddesc&action=edit reuploaddesc]<br>
+[[MediaWiki_talk:Reuploaddesc|Talk]]
+</td><td>
+Return to the upload form.
+</td><td>
+{{int:Reuploaddesc}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Reverted&action=edit reverted]<br>
+[[MediaWiki_talk:Reverted|Talk]]
+</td><td>
+Reverted to earlier revision
+</td><td>
+{{int:Reverted}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Revertimg&action=edit revertimg]<br>
+[[MediaWiki_talk:Revertimg|Talk]]
+</td><td>
+rev
+</td><td>
+{{int:Revertimg}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Revertpage&action=edit revertpage]<br>
+[[MediaWiki_talk:Revertpage|Talk]]
+</td><td>
+Reverted edit of $2, changed back to last version by $1
+</td><td>
+{{int:Revertpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Revhistory&action=edit revhistory]<br>
+[[MediaWiki_talk:Revhistory|Talk]]
+</td><td>
+Revision history
+</td><td>
+{{int:Revhistory}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Revisionasof&action=edit revisionasof]<br>
+[[MediaWiki_talk:Revisionasof|Talk]]
+</td><td>
+Revision as of $1
+</td><td>
+{{int:Revisionasof}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Revnotfound&action=edit revnotfound]<br>
+[[MediaWiki_talk:Revnotfound|Talk]]
+</td><td>
+Revision not found
+</td><td>
+{{int:Revnotfound}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Revnotfoundtext&action=edit revnotfoundtext]<br>
+[[MediaWiki_talk:Revnotfoundtext|Talk]]
+</td><td>
+The old revision of the page you asked for could not be found.
+Please check the URL you used to access this page.
+
+</td><td>
+{{int:Revnotfoundtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rfcurl&action=edit rfcurl]<br>
+[[MediaWiki_talk:Rfcurl|Talk]]
+</td><td>
+http&#58;//www.faqs.org/rfcs/rfc$1.html
+</td><td>
+{{int:Rfcurl}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rights&action=edit rights]<br>
+[[MediaWiki_talk:Rights|Talk]]
+</td><td>
+Rights:
+</td><td>
+{{int:Rights}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rollback&action=edit rollback]<br>
+[[MediaWiki_talk:Rollback|Talk]]
+</td><td>
+Roll back edits
+</td><td>
+{{int:Rollback}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rollback_short&action=edit rollback_short]<br>
+[[MediaWiki_talk:Rollback_short|Talk]]
+</td><td>
+Rollback
+</td><td>
+{{int:Rollback_short}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rollbackfailed&action=edit rollbackfailed]<br>
+[[MediaWiki_talk:Rollbackfailed|Talk]]
+</td><td>
+Rollback failed
+</td><td>
+{{int:Rollbackfailed}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rollbacklink&action=edit rollbacklink]<br>
+[[MediaWiki_talk:Rollbacklink|Talk]]
+</td><td>
+rollback
+</td><td>
+{{int:Rollbacklink}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Rows&action=edit rows]<br>
+[[MediaWiki_talk:Rows|Talk]]
+</td><td>
+Rows
+</td><td>
+{{int:Rows}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Savearticle&action=edit savearticle]<br>
+[[MediaWiki_talk:Savearticle|Talk]]
+</td><td>
+Save page
+</td><td>
+{{int:Savearticle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Savedprefs&action=edit savedprefs]<br>
+[[MediaWiki_talk:Savedprefs|Talk]]
+</td><td>
+Your preferences have been saved.
+</td><td>
+{{int:Savedprefs}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Savefile&action=edit savefile]<br>
+[[MediaWiki_talk:Savefile|Talk]]
+</td><td>
+Save file
+</td><td>
+{{int:Savefile}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Saveprefs&action=edit saveprefs]<br>
+[[MediaWiki_talk:Saveprefs|Talk]]
+</td><td>
+Save preferences
+</td><td>
+{{int:Saveprefs}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Search&action=edit search]<br>
+[[MediaWiki_talk:Search|Talk]]
+</td><td>
+Search
+</td><td>
+{{int:Search}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchdisabled&action=edit searchdisabled]<br>
+[[MediaWiki_talk:Searchdisabled|Talk]]
+</td><td>
+&lt;p&gt;Sorry! Full text search has been disabled temporarily, for performance reasons. In the meantime, you can use the Google search below, which may be out of date.&lt;/p&gt;
+</td><td>
+{{int:Searchdisabled}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchhelppage&action=edit searchhelppage]<br>
+[[MediaWiki_talk:Searchhelppage|Talk]]
+</td><td>
+Wiktionary:Searching
+</td><td>
+{{int:Searchhelppage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchingwikipedia&action=edit searchingwikipedia]<br>
+[[MediaWiki_talk:Searchingwikipedia|Talk]]
+</td><td>
+Searching Wiktionary
+</td><td>
+{{int:Searchingwikipedia}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchquery&action=edit searchquery]<br>
+[[MediaWiki_talk:Searchquery|Talk]]
+</td><td>
+For query &quot;$1&quot;
+</td><td>
+{{int:Searchquery}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchresults&action=edit searchresults]<br>
+[[MediaWiki_talk:Searchresults|Talk]]
+</td><td>
+Search results
+</td><td>
+{{int:Searchresults}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchresultshead&action=edit searchresultshead]<br>
+[[MediaWiki_talk:Searchresultshead|Talk]]
+</td><td>
+Search result settings
+</td><td>
+{{int:Searchresultshead}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Searchresulttext&action=edit searchresulttext]<br>
+[[MediaWiki_talk:Searchresulttext|Talk]]
+</td><td>
+For more information about searching Wiktionary, see $1.
+</td><td>
+{{int:Searchresulttext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sectionedit&action=edit sectionedit]<br>
+[[MediaWiki_talk:Sectionedit|Talk]]
+</td><td>
+ (section)
+</td><td>
+{{int:Sectionedit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Selectnewerversionfordiff&action=edit selectnewerversionfordiff]<br>
+[[MediaWiki_talk:Selectnewerversionfordiff|Talk]]
+</td><td>
+Select a newer version for comparison
+</td><td>
+{{int:Selectnewerversionfordiff}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Selectolderversionfordiff&action=edit selectolderversionfordiff]<br>
+[[MediaWiki_talk:Selectolderversionfordiff|Talk]]
+</td><td>
+Select an older version for comparison
+</td><td>
+{{int:Selectolderversionfordiff}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Selectonly&action=edit selectonly]<br>
+[[MediaWiki_talk:Selectonly|Talk]]
+</td><td>
+Only read-only queries are allowed.
+</td><td>
+{{int:Selectonly}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Selflinks&action=edit selflinks]<br>
+[[MediaWiki_talk:Selflinks|Talk]]
+</td><td>
+Pages with Self Links
+</td><td>
+{{int:Selflinks}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Selflinkstext&action=edit selflinkstext]<br>
+[[MediaWiki_talk:Selflinkstext|Talk]]
+</td><td>
+The following pages contain a link to themselves, which they should not.
+</td><td>
+{{int:Selflinkstext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Seriousxhtmlerrors&action=edit seriousxhtmlerrors]<br>
+[[MediaWiki_talk:Seriousxhtmlerrors|Talk]]
+</td><td>
+There were serious xhtml markup errors detected by tidy.
+</td><td>
+{{int:Seriousxhtmlerrors}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Servertime&action=edit servertime]<br>
+[[MediaWiki_talk:Servertime|Talk]]
+</td><td>
+Server time is now
+</td><td>
+{{int:Servertime}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Set_rights_fail&action=edit set_rights_fail]<br>
+[[MediaWiki_talk:Set_rights_fail|Talk]]
+</td><td>
+&lt;b&gt;User rights for &quot;$1&quot; could not be set. (Did you enter the name correctly?)&lt;/b&gt;
+</td><td>
+{{int:Set_rights_fail}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Set_user_rights&action=edit set_user_rights]<br>
+[[MediaWiki_talk:Set_user_rights|Talk]]
+</td><td>
+Set user rights
+</td><td>
+{{int:Set_user_rights}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Setbureaucratflag&action=edit setbureaucratflag]<br>
+[[MediaWiki_talk:Setbureaucratflag|Talk]]
+</td><td>
+Set bureaucrat flag
+</td><td>
+{{int:Setbureaucratflag}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Shortpages&action=edit shortpages]<br>
+[[MediaWiki_talk:Shortpages|Talk]]
+</td><td>
+Short pages
+</td><td>
+{{int:Shortpages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Show&action=edit show]<br>
+[[MediaWiki_talk:Show|Talk]]
+</td><td>
+show
+</td><td>
+{{int:Show}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Showhideminor&action=edit showhideminor]<br>
+[[MediaWiki_talk:Showhideminor|Talk]]
+</td><td>
+$1 minor edits &#124; $2 bots &#124; $3 logged in users
+</td><td>
+{{int:Showhideminor}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Showingresults&action=edit showingresults]<br>
+[[MediaWiki_talk:Showingresults|Talk]]
+</td><td>
+Showing below &lt;b&gt;$1&lt;/b&gt; results starting with #&lt;b&gt;$2&lt;/b&gt;.
+</td><td>
+{{int:Showingresults}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Showingresultsnum&action=edit showingresultsnum]<br>
+[[MediaWiki_talk:Showingresultsnum|Talk]]
+</td><td>
+Showing below &lt;b&gt;$3&lt;/b&gt; results starting with #&lt;b&gt;$2&lt;/b&gt;.
+</td><td>
+{{int:Showingresultsnum}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Showlast&action=edit showlast]<br>
+[[MediaWiki_talk:Showlast|Talk]]
+</td><td>
+Show last $1 images sorted $2.
+</td><td>
+{{int:Showlast}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Showpreview&action=edit showpreview]<br>
+[[MediaWiki_talk:Showpreview|Talk]]
+</td><td>
+Show preview
+</td><td>
+{{int:Showpreview}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Showtoc&action=edit showtoc]<br>
+[[MediaWiki_talk:Showtoc|Talk]]
+</td><td>
+show
+</td><td>
+{{int:Showtoc}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sig_tip&action=edit sig_tip]<br>
+[[MediaWiki_talk:Sig_tip|Talk]]
+</td><td>
+Your signature with timestamp
+</td><td>
+{{int:Sig_tip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sitestats&action=edit sitestats]<br>
+[[MediaWiki_talk:Sitestats|Talk]]
+</td><td>
+Site statistics
+</td><td>
+{{int:Sitestats}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sitestatstext&action=edit sitestatstext]<br>
+[[MediaWiki_talk:Sitestatstext|Talk]]
+</td><td>
+There are &#39;&#39;&#39;$1&#39;&#39;&#39; total pages in the database.
+This includes &quot;talk&quot; pages, pages about Wiktionary, minimal &quot;stub&quot;
+pages, redirects, and others that probably don&#39;t qualify as content pages.
+Excluding those, there are &#39;&#39;&#39;$2&#39;&#39;&#39; pages that are probably legitimate
+content pages.
+
+There have been a total of &#39;&#39;&#39;$3&#39;&#39;&#39; page views, and &#39;&#39;&#39;$4&#39;&#39;&#39; page edits
+since the wiki was setup.
+That comes to &#39;&#39;&#39;$5&#39;&#39;&#39; average edits per page, and &#39;&#39;&#39;$6&#39;&#39;&#39; views per edit.
+</td><td>
+{{int:Sitestatstext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sitesubtitle&action=edit sitesubtitle]<br>
+[[MediaWiki_talk:Sitesubtitle|Talk]]
+</td><td>
+The Free Encyclopedia
+</td><td>
+{{int:Sitesubtitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sitesupport&action=edit sitesupport]<br>
+[[MediaWiki_talk:Sitesupport|Talk]]
+</td><td>
+Donations
+</td><td>
+{{int:Sitesupport}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sitetitle&action=edit sitetitle]<br>
+[[MediaWiki_talk:Sitetitle|Talk]]
+</td><td>
+Wiktionary
+</td><td>
+{{int:Sitetitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Siteuser&action=edit siteuser]<br>
+[[MediaWiki_talk:Siteuser|Talk]]
+</td><td>
+Wiktionary user $1
+</td><td>
+{{int:Siteuser}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Siteusers&action=edit siteusers]<br>
+[[MediaWiki_talk:Siteusers|Talk]]
+</td><td>
+Wiktionary user(s) $1
+</td><td>
+{{int:Siteusers}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Skin&action=edit skin]<br>
+[[MediaWiki_talk:Skin|Talk]]
+</td><td>
+Skin
+</td><td>
+{{int:Skin}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Spamprotectiontext&action=edit spamprotectiontext]<br>
+[[MediaWiki_talk:Spamprotectiontext|Talk]]
+</td><td>
+The page you wanted to save was blocked by the spam filter. This is probably caused by a link to an external site.
+
+You might want to check the following regular expression for patterns that are currently blocked:
+</td><td>
+{{int:Spamprotectiontext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Spamprotectiontitle&action=edit spamprotectiontitle]<br>
+[[MediaWiki_talk:Spamprotectiontitle|Talk]]
+</td><td>
+Spam protection filter
+</td><td>
+{{int:Spamprotectiontitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Specialpage&action=edit specialpage]<br>
+[[MediaWiki_talk:Specialpage|Talk]]
+</td><td>
+Special Page
+</td><td>
+{{int:Specialpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Specialpages&action=edit specialpages]<br>
+[[MediaWiki_talk:Specialpages|Talk]]
+</td><td>
+Special pages
+</td><td>
+{{int:Specialpages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Spheading&action=edit spheading]<br>
+[[MediaWiki_talk:Spheading|Talk]]
+</td><td>
+Special pages for all users
+</td><td>
+{{int:Spheading}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sqlislogged&action=edit sqlislogged]<br>
+[[MediaWiki_talk:Sqlislogged|Talk]]
+</td><td>
+Please note that all queries are logged.
+</td><td>
+{{int:Sqlislogged}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sqlquery&action=edit sqlquery]<br>
+[[MediaWiki_talk:Sqlquery|Talk]]
+</td><td>
+Enter query
+</td><td>
+{{int:Sqlquery}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Statistics&action=edit statistics]<br>
+[[MediaWiki_talk:Statistics|Talk]]
+</td><td>
+Statistics
+</td><td>
+{{int:Statistics}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Storedversion&action=edit storedversion]<br>
+[[MediaWiki_talk:Storedversion|Talk]]
+</td><td>
+Stored version
+</td><td>
+{{int:Storedversion}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Stubthreshold&action=edit stubthreshold]<br>
+[[MediaWiki_talk:Stubthreshold|Talk]]
+</td><td>
+Threshold for stub display
+</td><td>
+{{int:Stubthreshold}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Subcategories&action=edit subcategories]<br>
+[[MediaWiki_talk:Subcategories|Talk]]
+</td><td>
+Subcategories
+</td><td>
+{{int:Subcategories}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Subject&action=edit subject]<br>
+[[MediaWiki_talk:Subject|Talk]]
+</td><td>
+Subject/headline
+</td><td>
+{{int:Subject}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Subjectpage&action=edit subjectpage]<br>
+[[MediaWiki_talk:Subjectpage|Talk]]
+</td><td>
+View subject
+</td><td>
+{{int:Subjectpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Successfulupload&action=edit successfulupload]<br>
+[[MediaWiki_talk:Successfulupload|Talk]]
+</td><td>
+Successful upload
+</td><td>
+{{int:Successfulupload}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Summary&action=edit summary]<br>
+[[MediaWiki_talk:Summary|Talk]]
+</td><td>
+Summary
+</td><td>
+{{int:Summary}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sysopspheading&action=edit sysopspheading]<br>
+[[MediaWiki_talk:Sysopspheading|Talk]]
+</td><td>
+For sysop use only
+</td><td>
+{{int:Sysopspheading}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sysoptext&action=edit sysoptext]<br>
+[[MediaWiki_talk:Sysoptext|Talk]]
+</td><td>
+The action you have requested can only be
+performed by users with &quot;sysop&quot; status.
+See $1.
+</td><td>
+{{int:Sysoptext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Sysoptitle&action=edit sysoptitle]<br>
+[[MediaWiki_talk:Sysoptitle|Talk]]
+</td><td>
+Sysop access required
+</td><td>
+{{int:Sysoptitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tableform&action=edit tableform]<br>
+[[MediaWiki_talk:Tableform|Talk]]
+</td><td>
+table
+</td><td>
+{{int:Tableform}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Talk&action=edit talk]<br>
+[[MediaWiki_talk:Talk|Talk]]
+</td><td>
+Discussion
+</td><td>
+{{int:Talk}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Talkexists&action=edit talkexists]<br>
+[[MediaWiki_talk:Talkexists|Talk]]
+</td><td>
+The page itself was moved successfully, but the
+talk page could not be moved because one already exists at the new
+title. Please merge them manually.
+</td><td>
+{{int:Talkexists}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Talkpage&action=edit talkpage]<br>
+[[MediaWiki_talk:Talkpage|Talk]]
+</td><td>
+Discuss this page
+</td><td>
+{{int:Talkpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Talkpagemoved&action=edit talkpagemoved]<br>
+[[MediaWiki_talk:Talkpagemoved|Talk]]
+</td><td>
+The corresponding talk page was also moved.
+</td><td>
+{{int:Talkpagemoved}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Talkpagenotmoved&action=edit talkpagenotmoved]<br>
+[[MediaWiki_talk:Talkpagenotmoved|Talk]]
+</td><td>
+The corresponding talk page was &lt;strong&gt;not&lt;/strong&gt; moved.
+</td><td>
+{{int:Talkpagenotmoved}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Talkpagetext&action=edit talkpagetext]<br>
+[[MediaWiki_talk:Talkpagetext|Talk]]
+</td><td>
+&lt;!-- MediaWiki:talkpagetext --&gt;
+</td><td>
+{{int:Talkpagetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Textboxsize&action=edit textboxsize]<br>
+[[MediaWiki_talk:Textboxsize|Talk]]
+</td><td>
+Textbox dimensions
+</td><td>
+{{int:Textboxsize}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Textmatches&action=edit textmatches]<br>
+[[MediaWiki_talk:Textmatches|Talk]]
+</td><td>
+Page text matches
+</td><td>
+{{int:Textmatches}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Thisisdeleted&action=edit thisisdeleted]<br>
+[[MediaWiki_talk:Thisisdeleted|Talk]]
+</td><td>
+View or restore $1?
+</td><td>
+{{int:Thisisdeleted}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Thumbnail-more&action=edit thumbnail-more]<br>
+[[MediaWiki_talk:Thumbnail-more|Talk]]
+</td><td>
+Enlarge
+</td><td>
+{{int:Thumbnail-more}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Timezonelegend&action=edit timezonelegend]<br>
+[[MediaWiki_talk:Timezonelegend|Talk]]
+</td><td>
+Time zone
+</td><td>
+{{int:Timezonelegend}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Timezoneoffset&action=edit timezoneoffset]<br>
+[[MediaWiki_talk:Timezoneoffset|Talk]]
+</td><td>
+Offset
+</td><td>
+{{int:Timezoneoffset}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Timezonetext&action=edit timezonetext]<br>
+[[MediaWiki_talk:Timezonetext|Talk]]
+</td><td>
+Enter number of hours your local time differs
+from server time (UTC).
+</td><td>
+{{int:Timezonetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Titlematches&action=edit titlematches]<br>
+[[MediaWiki_talk:Titlematches|Talk]]
+</td><td>
+Article title matches
+</td><td>
+{{int:Titlematches}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Toc&action=edit toc]<br>
+[[MediaWiki_talk:Toc|Talk]]
+</td><td>
+Table of contents
+</td><td>
+{{int:Toc}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Toolbox&action=edit toolbox]<br>
+[[MediaWiki_talk:Toolbox|Talk]]
+</td><td>
+Toolbox
+</td><td>
+{{int:Toolbox}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-addsection&action=edit tooltip-addsection]<br>
+[[MediaWiki_talk:Tooltip-addsection|Talk]]
+</td><td>
+Add a comment to this page. &#91;alt-+]
+</td><td>
+{{int:Tooltip-addsection}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-anontalk&action=edit tooltip-anontalk]<br>
+[[MediaWiki_talk:Tooltip-anontalk|Talk]]
+</td><td>
+Discussion about edits from this ip address &#91;alt-n]
+</td><td>
+{{int:Tooltip-anontalk}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-anonuserpage&action=edit tooltip-anonuserpage]<br>
+[[MediaWiki_talk:Tooltip-anonuserpage|Talk]]
+</td><td>
+The user page for the ip you&#39;re editing as &#91;alt-.]
+</td><td>
+{{int:Tooltip-anonuserpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-article&action=edit tooltip-article]<br>
+[[MediaWiki_talk:Tooltip-article|Talk]]
+</td><td>
+View the content page &#91;alt-a]
+</td><td>
+{{int:Tooltip-article}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-atom&action=edit tooltip-atom]<br>
+[[MediaWiki_talk:Tooltip-atom|Talk]]
+</td><td>
+Atom feed for this page
+</td><td>
+{{int:Tooltip-atom}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-compareselectedversions&action=edit tooltip-compareselectedversions]<br>
+[[MediaWiki_talk:Tooltip-compareselectedversions|Talk]]
+</td><td>
+See the differences between the two selected versions of this page. &#91;alt-v]
+</td><td>
+{{int:Tooltip-compareselectedversions}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-contributions&action=edit tooltip-contributions]<br>
+[[MediaWiki_talk:Tooltip-contributions|Talk]]
+</td><td>
+View the list of contributions of this user
+</td><td>
+{{int:Tooltip-contributions}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-currentevents&action=edit tooltip-currentevents]<br>
+[[MediaWiki_talk:Tooltip-currentevents|Talk]]
+</td><td>
+Find background information on current events
+</td><td>
+{{int:Tooltip-currentevents}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-delete&action=edit tooltip-delete]<br>
+[[MediaWiki_talk:Tooltip-delete|Talk]]
+</td><td>
+Delete this page &#91;alt-d]
+</td><td>
+{{int:Tooltip-delete}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-edit&action=edit tooltip-edit]<br>
+[[MediaWiki_talk:Tooltip-edit|Talk]]
+</td><td>
+You can edit this page. Please use the preview button before saving. &#91;alt-e]
+</td><td>
+{{int:Tooltip-edit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-emailuser&action=edit tooltip-emailuser]<br>
+[[MediaWiki_talk:Tooltip-emailuser|Talk]]
+</td><td>
+Send a mail to this user
+</td><td>
+{{int:Tooltip-emailuser}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-help&action=edit tooltip-help]<br>
+[[MediaWiki_talk:Tooltip-help|Talk]]
+</td><td>
+The place to find out.
+</td><td>
+{{int:Tooltip-help}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-history&action=edit tooltip-history]<br>
+[[MediaWiki_talk:Tooltip-history|Talk]]
+</td><td>
+Past versions of this page, &#91;alt-h]
+</td><td>
+{{int:Tooltip-history}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-login&action=edit tooltip-login]<br>
+[[MediaWiki_talk:Tooltip-login|Talk]]
+</td><td>
+You are encouraged to log in, it is not mandatory however. &#91;alt-o]
+</td><td>
+{{int:Tooltip-login}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-logout&action=edit tooltip-logout]<br>
+[[MediaWiki_talk:Tooltip-logout|Talk]]
+</td><td>
+Log out &#91;alt-o]
+</td><td>
+{{int:Tooltip-logout}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-mainpage&action=edit tooltip-mainpage]<br>
+[[MediaWiki_talk:Tooltip-mainpage|Talk]]
+</td><td>
+Visit the Main Page &#91;alt-z]
+</td><td>
+{{int:Tooltip-mainpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-minoredit&action=edit tooltip-minoredit]<br>
+[[MediaWiki_talk:Tooltip-minoredit|Talk]]
+</td><td>
+Mark this as a minor edit &#91;alt-i]
+</td><td>
+{{int:Tooltip-minoredit}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-move&action=edit tooltip-move]<br>
+[[MediaWiki_talk:Tooltip-move|Talk]]
+</td><td>
+Move this page &#91;alt-m]
+</td><td>
+{{int:Tooltip-move}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-mycontris&action=edit tooltip-mycontris]<br>
+[[MediaWiki_talk:Tooltip-mycontris|Talk]]
+</td><td>
+List of my contributions &#91;alt-y]
+</td><td>
+{{int:Tooltip-mycontris}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-mytalk&action=edit tooltip-mytalk]<br>
+[[MediaWiki_talk:Tooltip-mytalk|Talk]]
+</td><td>
+My talk page &#91;alt-n]
+</td><td>
+{{int:Tooltip-mytalk}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-nomove&action=edit tooltip-nomove]<br>
+[[MediaWiki_talk:Tooltip-nomove|Talk]]
+</td><td>
+You don&#39;t have the permissions to move this page
+</td><td>
+{{int:Tooltip-nomove}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-portal&action=edit tooltip-portal]<br>
+[[MediaWiki_talk:Tooltip-portal|Talk]]
+</td><td>
+About the project, what you can do, where to find things
+</td><td>
+{{int:Tooltip-portal}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-preferences&action=edit tooltip-preferences]<br>
+[[MediaWiki_talk:Tooltip-preferences|Talk]]
+</td><td>
+My preferences
+</td><td>
+{{int:Tooltip-preferences}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-preview&action=edit tooltip-preview]<br>
+[[MediaWiki_talk:Tooltip-preview|Talk]]
+</td><td>
+Preview your changes, please use this before saving! &#91;alt-p]
+</td><td>
+{{int:Tooltip-preview}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-protect&action=edit tooltip-protect]<br>
+[[MediaWiki_talk:Tooltip-protect|Talk]]
+</td><td>
+Protect this page &#91;alt-=]
+</td><td>
+{{int:Tooltip-protect}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-randompage&action=edit tooltip-randompage]<br>
+[[MediaWiki_talk:Tooltip-randompage|Talk]]
+</td><td>
+Load a random page &#91;alt-x]
+</td><td>
+{{int:Tooltip-randompage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-recentchanges&action=edit tooltip-recentchanges]<br>
+[[MediaWiki_talk:Tooltip-recentchanges|Talk]]
+</td><td>
+The list of recent changes in the wiki. &#91;alt-r]
+</td><td>
+{{int:Tooltip-recentchanges}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-recentchangeslinked&action=edit tooltip-recentchangeslinked]<br>
+[[MediaWiki_talk:Tooltip-recentchangeslinked|Talk]]
+</td><td>
+Recent changes in pages linking to this page &#91;alt-c]
+</td><td>
+{{int:Tooltip-recentchangeslinked}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-rss&action=edit tooltip-rss]<br>
+[[MediaWiki_talk:Tooltip-rss|Talk]]
+</td><td>
+RSS feed for this page
+</td><td>
+{{int:Tooltip-rss}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-save&action=edit tooltip-save]<br>
+[[MediaWiki_talk:Tooltip-save|Talk]]
+</td><td>
+Save your changes &#91;alt-s]
+</td><td>
+{{int:Tooltip-save}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-search&action=edit tooltip-search]<br>
+[[MediaWiki_talk:Tooltip-search|Talk]]
+</td><td>
+Search this wiki &#91;alt-f]
+</td><td>
+{{int:Tooltip-search}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-sitesupport&action=edit tooltip-sitesupport]<br>
+[[MediaWiki_talk:Tooltip-sitesupport|Talk]]
+</td><td>
+Support Wiktionary
+</td><td>
+{{int:Tooltip-sitesupport}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-specialpage&action=edit tooltip-specialpage]<br>
+[[MediaWiki_talk:Tooltip-specialpage|Talk]]
+</td><td>
+This is a special page, you can&#39;t edit the page itself.
+</td><td>
+{{int:Tooltip-specialpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-specialpages&action=edit tooltip-specialpages]<br>
+[[MediaWiki_talk:Tooltip-specialpages|Talk]]
+</td><td>
+List of all special pages &#91;alt-q]
+</td><td>
+{{int:Tooltip-specialpages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-talk&action=edit tooltip-talk]<br>
+[[MediaWiki_talk:Tooltip-talk|Talk]]
+</td><td>
+Discussion about the content page &#91;alt-t]
+</td><td>
+{{int:Tooltip-talk}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-undelete&action=edit tooltip-undelete]<br>
+[[MediaWiki_talk:Tooltip-undelete|Talk]]
+</td><td>
+Restore the $1 edits done to this page before it was deleted &#91;alt-d]
+</td><td>
+{{int:Tooltip-undelete}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-unwatch&action=edit tooltip-unwatch]<br>
+[[MediaWiki_talk:Tooltip-unwatch|Talk]]
+</td><td>
+Remove this page from your watchlist &#91;alt-w]
+</td><td>
+{{int:Tooltip-unwatch}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-upload&action=edit tooltip-upload]<br>
+[[MediaWiki_talk:Tooltip-upload|Talk]]
+</td><td>
+Upload images or media files &#91;alt-u]
+</td><td>
+{{int:Tooltip-upload}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-userpage&action=edit tooltip-userpage]<br>
+[[MediaWiki_talk:Tooltip-userpage|Talk]]
+</td><td>
+My user page &#91;alt-.]
+</td><td>
+{{int:Tooltip-userpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-viewsource&action=edit tooltip-viewsource]<br>
+[[MediaWiki_talk:Tooltip-viewsource|Talk]]
+</td><td>
+This page is protected. You can view its source. &#91;alt-e]
+</td><td>
+{{int:Tooltip-viewsource}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-watch&action=edit tooltip-watch]<br>
+[[MediaWiki_talk:Tooltip-watch|Talk]]
+</td><td>
+Add this page to your watchlist &#91;alt-w]
+</td><td>
+{{int:Tooltip-watch}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-watchlist&action=edit tooltip-watchlist]<br>
+[[MediaWiki_talk:Tooltip-watchlist|Talk]]
+</td><td>
+The list of pages you&#39;re monitoring for changes. &#91;alt-l]
+</td><td>
+{{int:Tooltip-watchlist}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Tooltip-whatlinkshere&action=edit tooltip-whatlinkshere]<br>
+[[MediaWiki_talk:Tooltip-whatlinkshere|Talk]]
+</td><td>
+List of all wiki pages that link here &#91;alt-b]
+</td><td>
+{{int:Tooltip-whatlinkshere}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uclinks&action=edit uclinks]<br>
+[[MediaWiki_talk:Uclinks|Talk]]
+</td><td>
+View the last $1 changes; view the last $2 days.
+</td><td>
+{{int:Uclinks}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Ucnote&action=edit ucnote]<br>
+[[MediaWiki_talk:Ucnote|Talk]]
+</td><td>
+Below are this user&#39;s last &lt;b&gt;$1&lt;/b&gt; changes in the last &lt;b&gt;$2&lt;/b&gt; days.
+</td><td>
+{{int:Ucnote}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uctop&action=edit uctop]<br>
+[[MediaWiki_talk:Uctop|Talk]]
+</td><td>
+ (top)
+</td><td>
+{{int:Uctop}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unblockip&action=edit unblockip]<br>
+[[MediaWiki_talk:Unblockip|Talk]]
+</td><td>
+Unblock user
+</td><td>
+{{int:Unblockip}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unblockiptext&action=edit unblockiptext]<br>
+[[MediaWiki_talk:Unblockiptext|Talk]]
+</td><td>
+Use the form below to restore write access
+to a previously blocked IP address or username.
+</td><td>
+{{int:Unblockiptext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unblocklink&action=edit unblocklink]<br>
+[[MediaWiki_talk:Unblocklink|Talk]]
+</td><td>
+unblock
+</td><td>
+{{int:Unblocklink}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unblocklogentry&action=edit unblocklogentry]<br>
+[[MediaWiki_talk:Unblocklogentry|Talk]]
+</td><td>
+unblocked &quot;$1&quot;
+</td><td>
+{{int:Unblocklogentry}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undelete&action=edit undelete]<br>
+[[MediaWiki_talk:Undelete|Talk]]
+</td><td>
+Restore deleted page
+</td><td>
+{{int:Undelete}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undelete_short&action=edit undelete_short]<br>
+[[MediaWiki_talk:Undelete_short|Talk]]
+</td><td>
+Undelete $1 edits
+</td><td>
+{{int:Undelete_short}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletearticle&action=edit undeletearticle]<br>
+[[MediaWiki_talk:Undeletearticle|Talk]]
+</td><td>
+Restore deleted page
+</td><td>
+{{int:Undeletearticle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletebtn&action=edit undeletebtn]<br>
+[[MediaWiki_talk:Undeletebtn|Talk]]
+</td><td>
+Restore!
+</td><td>
+{{int:Undeletebtn}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletedarticle&action=edit undeletedarticle]<br>
+[[MediaWiki_talk:Undeletedarticle|Talk]]
+</td><td>
+restored &quot;$1&quot;
+</td><td>
+{{int:Undeletedarticle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletedtext&action=edit undeletedtext]<br>
+[[MediaWiki_talk:Undeletedtext|Talk]]
+</td><td>
+&#91;&#91;$1]] has been successfully restored.
+See &#91;&#91;Wiktionary:Deletion_log]] for a record of recent deletions and restorations.
+</td><td>
+{{int:Undeletedtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletehistory&action=edit undeletehistory]<br>
+[[MediaWiki_talk:Undeletehistory|Talk]]
+</td><td>
+If you restore the page, all revisions will be restored to the history.
+If a new page with the same name has been created since the deletion, the restored
+revisions will appear in the prior history, and the current revision of the live page
+will not be automatically replaced.
+</td><td>
+{{int:Undeletehistory}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletepage&action=edit undeletepage]<br>
+[[MediaWiki_talk:Undeletepage|Talk]]
+</td><td>
+View and restore deleted pages
+</td><td>
+{{int:Undeletepage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeletepagetext&action=edit undeletepagetext]<br>
+[[MediaWiki_talk:Undeletepagetext|Talk]]
+</td><td>
+The following pages have been deleted but are still in the archive and
+can be restored. The archive may be periodically cleaned out.
+</td><td>
+{{int:Undeletepagetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeleterevision&action=edit undeleterevision]<br>
+[[MediaWiki_talk:Undeleterevision|Talk]]
+</td><td>
+Deleted revision as of $1
+</td><td>
+{{int:Undeleterevision}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Undeleterevisions&action=edit undeleterevisions]<br>
+[[MediaWiki_talk:Undeleterevisions|Talk]]
+</td><td>
+$1 revisions archived
+</td><td>
+{{int:Undeleterevisions}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unexpected&action=edit unexpected]<br>
+[[MediaWiki_talk:Unexpected|Talk]]
+</td><td>
+Unexpected value: &quot;$1&quot;=&quot;$2&quot;.
+</td><td>
+{{int:Unexpected}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unlockbtn&action=edit unlockbtn]<br>
+[[MediaWiki_talk:Unlockbtn|Talk]]
+</td><td>
+Unlock database
+</td><td>
+{{int:Unlockbtn}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unlockconfirm&action=edit unlockconfirm]<br>
+[[MediaWiki_talk:Unlockconfirm|Talk]]
+</td><td>
+Yes, I really want to unlock the database.
+</td><td>
+{{int:Unlockconfirm}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unlockdb&action=edit unlockdb]<br>
+[[MediaWiki_talk:Unlockdb|Talk]]
+</td><td>
+Unlock database
+</td><td>
+{{int:Unlockdb}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unlockdbsuccesssub&action=edit unlockdbsuccesssub]<br>
+[[MediaWiki_talk:Unlockdbsuccesssub|Talk]]
+</td><td>
+Database lock removed
+</td><td>
+{{int:Unlockdbsuccesssub}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unlockdbsuccesstext&action=edit unlockdbsuccesstext]<br>
+[[MediaWiki_talk:Unlockdbsuccesstext|Talk]]
+</td><td>
+The database has been unlocked.
+</td><td>
+{{int:Unlockdbsuccesstext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unlockdbtext&action=edit unlockdbtext]<br>
+[[MediaWiki_talk:Unlockdbtext|Talk]]
+</td><td>
+Unlocking the database will restore the ability of all
+users to edit pages, change their preferences, edit their watchlists, and
+other things requiring changes in the database.
+Please confirm that this is what you intend to do.
+</td><td>
+{{int:Unlockdbtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unprotect&action=edit unprotect]<br>
+[[MediaWiki_talk:Unprotect|Talk]]
+</td><td>
+Unprotect
+</td><td>
+{{int:Unprotect}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unprotectcomment&action=edit unprotectcomment]<br>
+[[MediaWiki_talk:Unprotectcomment|Talk]]
+</td><td>
+Reason for unprotecting
+</td><td>
+{{int:Unprotectcomment}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unprotectedarticle&action=edit unprotectedarticle]<br>
+[[MediaWiki_talk:Unprotectedarticle|Talk]]
+</td><td>
+unprotected &#91;&#91;$1]]
+</td><td>
+{{int:Unprotectedarticle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unprotectsub&action=edit unprotectsub]<br>
+[[MediaWiki_talk:Unprotectsub|Talk]]
+</td><td>
+(Unprotecting &quot;$1&quot;)
+</td><td>
+{{int:Unprotectsub}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unprotectthispage&action=edit unprotectthispage]<br>
+[[MediaWiki_talk:Unprotectthispage|Talk]]
+</td><td>
+Unprotect this page
+</td><td>
+{{int:Unprotectthispage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unusedimages&action=edit unusedimages]<br>
+[[MediaWiki_talk:Unusedimages|Talk]]
+</td><td>
+Unused images
+</td><td>
+{{int:Unusedimages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unusedimagestext&action=edit unusedimagestext]<br>
+[[MediaWiki_talk:Unusedimagestext|Talk]]
+</td><td>
+&lt;p&gt;Please note that other web sites may link to an image with
+a direct URL, and so may still be listed here despite being
+in active use.
+</td><td>
+{{int:Unusedimagestext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unwatch&action=edit unwatch]<br>
+[[MediaWiki_talk:Unwatch|Talk]]
+</td><td>
+Unwatch
+</td><td>
+{{int:Unwatch}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Unwatchthispage&action=edit unwatchthispage]<br>
+[[MediaWiki_talk:Unwatchthispage|Talk]]
+</td><td>
+Stop watching
+</td><td>
+{{int:Unwatchthispage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Updated&action=edit updated]<br>
+[[MediaWiki_talk:Updated|Talk]]
+</td><td>
+(Updated)
+</td><td>
+{{int:Updated}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Upload&action=edit upload]<br>
+[[MediaWiki_talk:Upload|Talk]]
+</td><td>
+Upload file
+</td><td>
+{{int:Upload}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadbtn&action=edit uploadbtn]<br>
+[[MediaWiki_talk:Uploadbtn|Talk]]
+</td><td>
+Upload file
+</td><td>
+{{int:Uploadbtn}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploaddisabled&action=edit uploaddisabled]<br>
+[[MediaWiki_talk:Uploaddisabled|Talk]]
+</td><td>
+Sorry, uploading is disabled.
+</td><td>
+{{int:Uploaddisabled}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadedfiles&action=edit uploadedfiles]<br>
+[[MediaWiki_talk:Uploadedfiles|Talk]]
+</td><td>
+Uploaded files
+</td><td>
+{{int:Uploadedfiles}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadedimage&action=edit uploadedimage]<br>
+[[MediaWiki_talk:Uploadedimage|Talk]]
+</td><td>
+uploaded &quot;$1&quot;
+</td><td>
+{{int:Uploadedimage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploaderror&action=edit uploaderror]<br>
+[[MediaWiki_talk:Uploaderror|Talk]]
+</td><td>
+Upload error
+</td><td>
+{{int:Uploaderror}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadfile&action=edit uploadfile]<br>
+[[MediaWiki_talk:Uploadfile|Talk]]
+</td><td>
+Upload images, sounds, documents etc.
+</td><td>
+{{int:Uploadfile}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadlink&action=edit uploadlink]<br>
+[[MediaWiki_talk:Uploadlink|Talk]]
+</td><td>
+Upload images
+</td><td>
+{{int:Uploadlink}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadlog&action=edit uploadlog]<br>
+[[MediaWiki_talk:Uploadlog|Talk]]
+</td><td>
+upload log
+</td><td>
+{{int:Uploadlog}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadlogpage&action=edit uploadlogpage]<br>
+[[MediaWiki_talk:Uploadlogpage|Talk]]
+</td><td>
+Upload_log
+</td><td>
+{{int:Uploadlogpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadlogpagetext&action=edit uploadlogpagetext]<br>
+[[MediaWiki_talk:Uploadlogpagetext|Talk]]
+</td><td>
+Below is a list of the most recent file uploads.
+All times shown are server time (UTC).
+&lt;ul&gt;
+&lt;/ul&gt;
+
+</td><td>
+{{int:Uploadlogpagetext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadnologin&action=edit uploadnologin]<br>
+[[MediaWiki_talk:Uploadnologin|Talk]]
+</td><td>
+Not logged in
+</td><td>
+{{int:Uploadnologin}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadnologintext&action=edit uploadnologintext]<br>
+[[MediaWiki_talk:Uploadnologintext|Talk]]
+</td><td>
+You must be &lt;a href=&quot;/wiki/Special:Userlogin&quot;&gt;logged in&lt;/a&gt;
+to upload files.
+</td><td>
+{{int:Uploadnologintext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadtext&action=edit uploadtext]<br>
+[[MediaWiki_talk:Uploadtext|Talk]]
+</td><td>
+&lt;strong&gt;STOP!&lt;/strong&gt; Before you upload here,
+make sure to read and follow the &lt;a href=&quot;/wiki/Special:Image_use_policy&quot;&gt;image use policy&lt;/a&gt;.
+&lt;p&gt;If a file with the name you are specifying already
+exists on the wiki, it&#39;ll be replaced without warning.
+So unless you mean to update a file, it&#39;s a good idea
+to first check if such a file exists.
+&lt;p&gt;To view or search previously uploaded images,
+go to the &lt;a href=&quot;/wiki/Special:Imagelist&quot;&gt;list of uploaded images&lt;/a&gt;.
+Uploads and deletions are logged on the &lt;a href=&quot;/wiki/Wiktionary:Upload_log&quot;&gt;upload log&lt;/a&gt;.
+&lt;/p&gt;&lt;p&gt;Use the form below to upload new image files for use in
+illustrating your pages.
+On most browsers, you will see a &quot;Browse...&quot; button, which will
+bring up your operating system&#39;s standard file open dialog.
+Choosing a file will fill the name of that file into the text
+field next to the button.
+You must also check the box affirming that you are not
+violating any copyrights by uploading the file.
+Press the &quot;Upload&quot; button to finish the upload.
+This may take some time if you have a slow internet connection.
+&lt;p&gt;The preferred formats are JPEG for photographic images, PNG
+for drawings and other iconic images, and OGG for sounds.
+Please name your files descriptively to avoid confusion.
+To include the image in a page, use a link in the form
+&lt;b&gt;&#91;&#91;Image:file.jpg]]&lt;/b&gt; or &lt;b&gt;&#91;&#91;Image:file.png&#124;alt text]]&lt;/b&gt;
+or &lt;b&gt;&#91;&#91;Media:file.ogg]]&lt;/b&gt; for sounds.
+&lt;p&gt;Please note that as with wiki pages, others may edit or
+delete your uploads if they think it serves the project, and
+you may be blocked from uploading if you abuse the system.
+</td><td>
+{{int:Uploadtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Uploadwarning&action=edit uploadwarning]<br>
+[[MediaWiki_talk:Uploadwarning|Talk]]
+</td><td>
+Upload warning
+</td><td>
+{{int:Uploadwarning}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:User_rights_set&action=edit user_rights_set]<br>
+[[MediaWiki_talk:User_rights_set|Talk]]
+</td><td>
+&lt;b&gt;User rights for &quot;$1&quot; updated&lt;/b&gt;
+</td><td>
+{{int:User_rights_set}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Usercssjs&action=edit usercssjs]<br>
+[[MediaWiki_talk:Usercssjs|Talk]]
+</td><td>
+&#39;&#39;&#39;Note:&#39;&#39;&#39; After saving, you have to tell your bowser to get the new version: &#39;&#39;&#39;Mozilla:&#39;&#39;&#39; click &#39;&#39;reload&#39;&#39;(or &#39;&#39;ctrl-r&#39;&#39;), &#39;&#39;&#39;IE / Opera:&#39;&#39;&#39; &#39;&#39;ctrl-f5&#39;&#39;, &#39;&#39;&#39;Safari:&#39;&#39;&#39; &#39;&#39;cmd-r&#39;&#39;, &#39;&#39;&#39;Konqueror&#39;&#39;&#39; &#39;&#39;ctrl-r&#39;&#39;.
+</td><td>
+{{int:Usercssjs}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Usercssjsyoucanpreview&action=edit usercssjsyoucanpreview]<br>
+[[MediaWiki_talk:Usercssjsyoucanpreview|Talk]]
+</td><td>
+&lt;strong&gt;Tip:&lt;/strong&gt; Use the &#39;Show preview&#39; button to test your new css/js before saving.
+</td><td>
+{{int:Usercssjsyoucanpreview}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Usercsspreview&action=edit usercsspreview]<br>
+[[MediaWiki_talk:Usercsspreview|Talk]]
+</td><td>
+&#39;&#39;&#39;Remember that you are only previewing your user css, it has not yet been saved!&#39;&#39;&#39;
+</td><td>
+{{int:Usercsspreview}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userexists&action=edit userexists]<br>
+[[MediaWiki_talk:Userexists|Talk]]
+</td><td>
+The user name you entered is already in use. Please choose a different name.
+</td><td>
+{{int:Userexists}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userjspreview&action=edit userjspreview]<br>
+[[MediaWiki_talk:Userjspreview|Talk]]
+</td><td>
+&#39;&#39;&#39;Remember that you are only testing/previewing your user javascript, it has not yet been saved!&#39;&#39;&#39;
+</td><td>
+{{int:Userjspreview}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userlogin&action=edit userlogin]<br>
+[[MediaWiki_talk:Userlogin|Talk]]
+</td><td>
+Log in
+</td><td>
+{{int:Userlogin}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userlogout&action=edit userlogout]<br>
+[[MediaWiki_talk:Userlogout|Talk]]
+</td><td>
+Log out
+</td><td>
+{{int:Userlogout}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Usermailererror&action=edit usermailererror]<br>
+[[MediaWiki_talk:Usermailererror|Talk]]
+</td><td>
+Mail object returned error:
+</td><td>
+{{int:Usermailererror}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userpage&action=edit userpage]<br>
+[[MediaWiki_talk:Userpage|Talk]]
+</td><td>
+View user page
+</td><td>
+{{int:Userpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userstats&action=edit userstats]<br>
+[[MediaWiki_talk:Userstats|Talk]]
+</td><td>
+User statistics
+</td><td>
+{{int:Userstats}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Userstatstext&action=edit userstatstext]<br>
+[[MediaWiki_talk:Userstatstext|Talk]]
+</td><td>
+There are &#39;&#39;&#39;$1&#39;&#39;&#39; registered users.
+&#39;&#39;&#39;$2&#39;&#39;&#39; of these are administrators (see $3).
+</td><td>
+{{int:Userstatstext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Version&action=edit version]<br>
+[[MediaWiki_talk:Version|Talk]]
+</td><td>
+Version
+</td><td>
+{{int:Version}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Viewcount&action=edit viewcount]<br>
+[[MediaWiki_talk:Viewcount|Talk]]
+</td><td>
+This page has been accessed $1 times.
+</td><td>
+{{int:Viewcount}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Viewprevnext&action=edit viewprevnext]<br>
+[[MediaWiki_talk:Viewprevnext|Talk]]
+</td><td>
+View ($1) ($2) ($3).
+</td><td>
+{{int:Viewprevnext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Viewsource&action=edit viewsource]<br>
+[[MediaWiki_talk:Viewsource|Talk]]
+</td><td>
+View source
+</td><td>
+{{int:Viewsource}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Viewtalkpage&action=edit viewtalkpage]<br>
+[[MediaWiki_talk:Viewtalkpage|Talk]]
+</td><td>
+View discussion
+</td><td>
+{{int:Viewtalkpage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wantedpages&action=edit wantedpages]<br>
+[[MediaWiki_talk:Wantedpages|Talk]]
+</td><td>
+Wanted pages
+</td><td>
+{{int:Wantedpages}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watch&action=edit watch]<br>
+[[MediaWiki_talk:Watch|Talk]]
+</td><td>
+Watch
+</td><td>
+{{int:Watch}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchdetails&action=edit watchdetails]<br>
+[[MediaWiki_talk:Watchdetails|Talk]]
+</td><td>
+($1 pages watched not counting talk pages;
+$2 total pages edited since cutoff;
+$3...
+&lt;a href=&#39;$4&#39;&gt;show and edit complete list&lt;/a&gt;.)
+</td><td>
+{{int:Watchdetails}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watcheditlist&action=edit watcheditlist]<br>
+[[MediaWiki_talk:Watcheditlist|Talk]]
+</td><td>
+Here&#39;s an alphabetical list of your
+watched pages. Check the boxes of pages you want to remove
+from your watchlist and click the &#39;remove checked&#39; button
+at the bottom of the screen.
+</td><td>
+{{int:Watcheditlist}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchlist&action=edit watchlist]<br>
+[[MediaWiki_talk:Watchlist|Talk]]
+</td><td>
+My watchlist
+</td><td>
+{{int:Watchlist}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchlistcontains&action=edit watchlistcontains]<br>
+[[MediaWiki_talk:Watchlistcontains|Talk]]
+</td><td>
+Your watchlist contains $1 pages.
+</td><td>
+{{int:Watchlistcontains}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchlistsub&action=edit watchlistsub]<br>
+[[MediaWiki_talk:Watchlistsub|Talk]]
+</td><td>
+(for user &quot;$1&quot;)
+</td><td>
+{{int:Watchlistsub}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchmethod-list&action=edit watchmethod-list]<br>
+[[MediaWiki_talk:Watchmethod-list|Talk]]
+</td><td>
+checking watched pages for recent edits
+</td><td>
+{{int:Watchmethod-list}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchmethod-recent&action=edit watchmethod-recent]<br>
+[[MediaWiki_talk:Watchmethod-recent|Talk]]
+</td><td>
+checking recent edits for watched pages
+</td><td>
+{{int:Watchmethod-recent}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchnochange&action=edit watchnochange]<br>
+[[MediaWiki_talk:Watchnochange|Talk]]
+</td><td>
+None of your watched items were edited in the time period displayed.
+</td><td>
+{{int:Watchnochange}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchnologin&action=edit watchnologin]<br>
+[[MediaWiki_talk:Watchnologin|Talk]]
+</td><td>
+Not logged in
+</td><td>
+{{int:Watchnologin}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchnologintext&action=edit watchnologintext]<br>
+[[MediaWiki_talk:Watchnologintext|Talk]]
+</td><td>
+You must be &lt;a href=&quot;/wiki/Special:Userlogin&quot;&gt;logged in&lt;/a&gt;
+to modify your watchlist.
+</td><td>
+{{int:Watchnologintext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchthis&action=edit watchthis]<br>
+[[MediaWiki_talk:Watchthis|Talk]]
+</td><td>
+Watch this page
+</td><td>
+{{int:Watchthis}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Watchthispage&action=edit watchthispage]<br>
+[[MediaWiki_talk:Watchthispage|Talk]]
+</td><td>
+Watch this page
+</td><td>
+{{int:Watchthispage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Welcomecreation&action=edit welcomecreation]<br>
+[[MediaWiki_talk:Welcomecreation|Talk]]
+</td><td>
+&lt;h2&gt;Welcome, $1!&lt;/h2&gt;&lt;p&gt;Your account has been created.
+Don&#39;t forget to change your Wiktionary preferences.
+</td><td>
+{{int:Welcomecreation}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whatlinkshere&action=edit whatlinkshere]<br>
+[[MediaWiki_talk:Whatlinkshere|Talk]]
+</td><td>
+What links here
+</td><td>
+{{int:Whatlinkshere}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whitelistacctext&action=edit whitelistacctext]<br>
+[[MediaWiki_talk:Whitelistacctext|Talk]]
+</td><td>
+To be allowed to create accounts in this Wiki you have to &#91;&#91;Special:Userlogin&#124;log]] in and have the appropriate permissions.
+</td><td>
+{{int:Whitelistacctext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whitelistacctitle&action=edit whitelistacctitle]<br>
+[[MediaWiki_talk:Whitelistacctitle|Talk]]
+</td><td>
+You are not allowed to create an account
+</td><td>
+{{int:Whitelistacctitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whitelistedittext&action=edit whitelistedittext]<br>
+[[MediaWiki_talk:Whitelistedittext|Talk]]
+</td><td>
+You have to &#91;&#91;Special:Userlogin&#124;login]] to edit pages.
+</td><td>
+{{int:Whitelistedittext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whitelistedittitle&action=edit whitelistedittitle]<br>
+[[MediaWiki_talk:Whitelistedittitle|Talk]]
+</td><td>
+Login required to edit
+</td><td>
+{{int:Whitelistedittitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whitelistreadtext&action=edit whitelistreadtext]<br>
+[[MediaWiki_talk:Whitelistreadtext|Talk]]
+</td><td>
+You have to &#91;&#91;Special:Userlogin&#124;login]] to read pages.
+</td><td>
+{{int:Whitelistreadtext}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Whitelistreadtitle&action=edit whitelistreadtitle]<br>
+[[MediaWiki_talk:Whitelistreadtitle|Talk]]
+</td><td>
+Login required to read
+</td><td>
+{{int:Whitelistreadtitle}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wikipediapage&action=edit wikipediapage]<br>
+[[MediaWiki_talk:Wikipediapage|Talk]]
+</td><td>
+View project page
+</td><td>
+{{int:Wikipediapage}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wikititlesuffix&action=edit wikititlesuffix]<br>
+[[MediaWiki_talk:Wikititlesuffix|Talk]]
+</td><td>
+Wiktionary
+</td><td>
+{{int:Wikititlesuffix}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wlnote&action=edit wlnote]<br>
+[[MediaWiki_talk:Wlnote|Talk]]
+</td><td>
+Below are the last $1 changes in the last &lt;b&gt;$2&lt;/b&gt; hours.
+</td><td>
+{{int:Wlnote}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wlsaved&action=edit wlsaved]<br>
+[[MediaWiki_talk:Wlsaved|Talk]]
+</td><td>
+This is a saved version of your watchlist.
+</td><td>
+{{int:Wlsaved}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wlshowlast&action=edit wlshowlast]<br>
+[[MediaWiki_talk:Wlshowlast|Talk]]
+</td><td>
+Show last $1 hours $2 days $3
+</td><td>
+{{int:Wlshowlast}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wrong_wfQuery_params&action=edit wrong_wfQuery_params]<br>
+[[MediaWiki_talk:Wrong_wfQuery_params|Talk]]
+</td><td>
+Incorrect parameters to wfQuery()&lt;br /&gt;
+Function: $1&lt;br /&gt;
+Query: $2
+
+</td><td>
+{{int:Wrong_wfQuery_params}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Wrongpassword&action=edit wrongpassword]<br>
+[[MediaWiki_talk:Wrongpassword|Talk]]
+</td><td>
+The password you entered is incorrect. Please try again.
+</td><td>
+{{int:Wrongpassword}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yourdiff&action=edit yourdiff]<br>
+[[MediaWiki_talk:Yourdiff|Talk]]
+</td><td>
+Differences
+</td><td>
+{{int:Yourdiff}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Youremail&action=edit youremail]<br>
+[[MediaWiki_talk:Youremail|Talk]]
+</td><td>
+Your email*
+</td><td>
+{{int:Youremail}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yourname&action=edit yourname]<br>
+[[MediaWiki_talk:Yourname|Talk]]
+</td><td>
+Your user name
+</td><td>
+{{int:Yourname}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yournick&action=edit yournick]<br>
+[[MediaWiki_talk:Yournick|Talk]]
+</td><td>
+Your nickname (for signatures)
+</td><td>
+{{int:Yournick}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yourpassword&action=edit yourpassword]<br>
+[[MediaWiki_talk:Yourpassword|Talk]]
+</td><td>
+Your password
+</td><td>
+{{int:Yourpassword}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yourpasswordagain&action=edit yourpasswordagain]<br>
+[[MediaWiki_talk:Yourpasswordagain|Talk]]
+</td><td>
+Retype password
+</td><td>
+{{int:Yourpasswordagain}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yourrealname&action=edit yourrealname]<br>
+[[MediaWiki_talk:Yourrealname|Talk]]
+</td><td>
+Your real name*
+</td><td>
+{{int:Yourrealname}}
+</td></tr><tr><td>
+[http://tl.wiktionary.org/w/wiki.phtml?title=MediaWiki:Yourtext&action=edit yourtext]<br>
+[[MediaWiki_talk:Yourtext|Talk]]
+</td><td>
+Your text
+</td><td>
+{{int:Yourtext}}
+</td></tr></table>
+
diff --git a/tests/parser/preprocess/Factorial.expected b/tests/parser/preprocess/Factorial.expected
new file mode 100644
index 00000000..099029c0
--- /dev/null
+++ b/tests/parser/preprocess/Factorial.expected
@@ -0,0 +1,17 @@
+<root><template lineStart="1"><title>#expr:<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=00</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>01<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=01</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*01<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=02</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*02<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=03</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*03<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=04</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*04<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=05</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*05<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=06</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*06<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=07</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*07<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=08</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*08<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=09</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*09<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=10</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*10<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=11</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*11<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=12</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*12<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=13</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*13<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=14</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*14<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=15</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*15<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=16</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*16<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=17</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*17<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=18</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*18<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=19</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*19<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=20</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*20<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=21</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*21<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=22</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*22<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=23</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*23<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=24</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*24<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=25</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*25<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=26</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*26<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=27</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*27<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=28</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*28<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=29</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*29<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=30</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*30<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=31</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*31<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=32</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*32<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=33</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*33<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=34</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*34<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=35</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*35<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=36</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*36<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=37</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*37<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=38</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*38<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=39</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*39<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=40</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*40<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=41</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*41<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=42</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*42<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=43</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*43<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=44</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*44<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=45</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*45<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=46</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*46<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=47</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*47<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=48</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*48<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=49</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*49<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=50</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*50<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=51</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*51<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=52</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*52<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=53</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*53<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=54</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*54<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=55</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*55<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=56</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*56<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=57</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*57<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=58</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*58<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=59</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*59<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=60</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*60<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=61</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*61<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=62</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*62<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=63</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*63<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=64</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*64<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=65</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*65<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=66</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*66<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=67</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*67<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=68</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*68<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=69</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*69<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=70</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*70<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=71</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*71<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=72</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*72<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=73</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*73<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=74</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*74<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=75</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*75<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=76</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*76<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=77</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*77<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=78</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*78<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=79</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*79<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=80</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*80<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=81</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*81<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=82</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*82<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=83</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*83<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=84</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*84<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=85</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*85<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=86</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*86<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=87</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*87<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=88</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*88<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=89</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*89<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=90</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*90<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=91</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*91<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=92</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*92<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=93</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*93<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=94</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*94<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=95</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*95<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=96</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*96<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=97</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*97<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=98</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*98<template><title>#ifeq:<template><title>#expr:<tplarg><title>1</title></tplarg>&gt;=99</title></template></title><part><name index="1" /><value>1</value></part><part><name index="2" /><value>*99</value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></value></part></template></title></template><ignore>&lt;noinclude&gt;</ignore>
+<template lineStart="1"><title>Template documentation</title></template>
+This template finds the [[factorial]] of a number. To use it, enter:&lt;br /&gt;
+&lt;code&gt;&lt;nowiki&gt;<template><title>factorial</title><part><name index="1" /><value>input</value></part></template>&lt;/nowiki&gt;&lt;/code&gt;&lt;br /&gt;
+The input must be a positive interger smaller than 100 (better than most calculators, which go up to only 69). This template works by repeating conditional multiplications. Examples:&lt;br /&gt;
+*&lt;nowiki&gt;<template><title>factorial</title><part><name index="1" /><value>2</value></part></template>&lt;/nowiki&gt; gives <template><title>factorial</title><part><name index="1" /><value>2</value></part></template>
+*&lt;nowiki&gt;<template><title>factorial</title><part><name index="1" /><value>3</value></part></template>&lt;/nowiki&gt; gives <template><title>factorial</title><part><name index="1" /><value>3</value></part></template>
+*&lt;nowiki&gt;<template><title>factorial</title><part><name index="1" /><value>5</value></part></template>&lt;/nowiki&gt; gives <template><title>factorial</title><part><name index="1" /><value>5</value></part></template>
+*&lt;nowiki&gt;<template><title>factorial</title><part><name index="1" /><value>10</value></part></template>&lt;/nowiki&gt; gives <template><title>factorial</title><part><name index="1" /><value>10</value></part></template>
+*&lt;nowiki&gt;<template><title>factorial</title><part><name index="1" /><value>80</value></part></template>&lt;/nowiki&gt; gives <template><title>factorial</title><part><name index="1" /><value>80</value></part></template>
+*&lt;nowiki&gt;<template><title>factorial</title><part><name index="1" /><value>0.5</value></part></template>&lt;/nowiki&gt; gives <template><title>factorial</title><part><name index="1" /><value>0.5</value></part></template> (invalid input)
+*&lt;nowiki&gt;<template><title>factorial</title><part><name index="1" /><value>-1</value></part></template>&lt;/nowiki&gt; gives <template><title>factorial</title><part><name index="1" /><value>-1</value></part></template> (invalid input)
+<template lineStart="1"><title>esoteric</title></template>
+[[Category:Mathematical templates|<template><title>PAGENAME</title></template>]]
+<ignore>&lt;/noinclude&gt;</ignore>
+
+</root> \ No newline at end of file
diff --git a/tests/parser/preprocess/Factorial.txt b/tests/parser/preprocess/Factorial.txt
new file mode 100644
index 00000000..316f0792
--- /dev/null
+++ b/tests/parser/preprocess/Factorial.txt
@@ -0,0 +1,16 @@
+{{#expr:{{#ifeq:{{#expr:{{{1}}}>=00}}|1|01{{#ifeq:{{#expr:{{{1}}}>=01}}|1|*01{{#ifeq:{{#expr:{{{1}}}>=02}}|1|*02{{#ifeq:{{#expr:{{{1}}}>=03}}|1|*03{{#ifeq:{{#expr:{{{1}}}>=04}}|1|*04{{#ifeq:{{#expr:{{{1}}}>=05}}|1|*05{{#ifeq:{{#expr:{{{1}}}>=06}}|1|*06{{#ifeq:{{#expr:{{{1}}}>=07}}|1|*07{{#ifeq:{{#expr:{{{1}}}>=08}}|1|*08{{#ifeq:{{#expr:{{{1}}}>=09}}|1|*09{{#ifeq:{{#expr:{{{1}}}>=10}}|1|*10{{#ifeq:{{#expr:{{{1}}}>=11}}|1|*11{{#ifeq:{{#expr:{{{1}}}>=12}}|1|*12{{#ifeq:{{#expr:{{{1}}}>=13}}|1|*13{{#ifeq:{{#expr:{{{1}}}>=14}}|1|*14{{#ifeq:{{#expr:{{{1}}}>=15}}|1|*15{{#ifeq:{{#expr:{{{1}}}>=16}}|1|*16{{#ifeq:{{#expr:{{{1}}}>=17}}|1|*17{{#ifeq:{{#expr:{{{1}}}>=18}}|1|*18{{#ifeq:{{#expr:{{{1}}}>=19}}|1|*19{{#ifeq:{{#expr:{{{1}}}>=20}}|1|*20{{#ifeq:{{#expr:{{{1}}}>=21}}|1|*21{{#ifeq:{{#expr:{{{1}}}>=22}}|1|*22{{#ifeq:{{#expr:{{{1}}}>=23}}|1|*23{{#ifeq:{{#expr:{{{1}}}>=24}}|1|*24{{#ifeq:{{#expr:{{{1}}}>=25}}|1|*25{{#ifeq:{{#expr:{{{1}}}>=26}}|1|*26{{#ifeq:{{#expr:{{{1}}}>=27}}|1|*27{{#ifeq:{{#expr:{{{1}}}>=28}}|1|*28{{#ifeq:{{#expr:{{{1}}}>=29}}|1|*29{{#ifeq:{{#expr:{{{1}}}>=30}}|1|*30{{#ifeq:{{#expr:{{{1}}}>=31}}|1|*31{{#ifeq:{{#expr:{{{1}}}>=32}}|1|*32{{#ifeq:{{#expr:{{{1}}}>=33}}|1|*33{{#ifeq:{{#expr:{{{1}}}>=34}}|1|*34{{#ifeq:{{#expr:{{{1}}}>=35}}|1|*35{{#ifeq:{{#expr:{{{1}}}>=36}}|1|*36{{#ifeq:{{#expr:{{{1}}}>=37}}|1|*37{{#ifeq:{{#expr:{{{1}}}>=38}}|1|*38{{#ifeq:{{#expr:{{{1}}}>=39}}|1|*39{{#ifeq:{{#expr:{{{1}}}>=40}}|1|*40{{#ifeq:{{#expr:{{{1}}}>=41}}|1|*41{{#ifeq:{{#expr:{{{1}}}>=42}}|1|*42{{#ifeq:{{#expr:{{{1}}}>=43}}|1|*43{{#ifeq:{{#expr:{{{1}}}>=44}}|1|*44{{#ifeq:{{#expr:{{{1}}}>=45}}|1|*45{{#ifeq:{{#expr:{{{1}}}>=46}}|1|*46{{#ifeq:{{#expr:{{{1}}}>=47}}|1|*47{{#ifeq:{{#expr:{{{1}}}>=48}}|1|*48{{#ifeq:{{#expr:{{{1}}}>=49}}|1|*49{{#ifeq:{{#expr:{{{1}}}>=50}}|1|*50{{#ifeq:{{#expr:{{{1}}}>=51}}|1|*51{{#ifeq:{{#expr:{{{1}}}>=52}}|1|*52{{#ifeq:{{#expr:{{{1}}}>=53}}|1|*53{{#ifeq:{{#expr:{{{1}}}>=54}}|1|*54{{#ifeq:{{#expr:{{{1}}}>=55}}|1|*55{{#ifeq:{{#expr:{{{1}}}>=56}}|1|*56{{#ifeq:{{#expr:{{{1}}}>=57}}|1|*57{{#ifeq:{{#expr:{{{1}}}>=58}}|1|*58{{#ifeq:{{#expr:{{{1}}}>=59}}|1|*59{{#ifeq:{{#expr:{{{1}}}>=60}}|1|*60{{#ifeq:{{#expr:{{{1}}}>=61}}|1|*61{{#ifeq:{{#expr:{{{1}}}>=62}}|1|*62{{#ifeq:{{#expr:{{{1}}}>=63}}|1|*63{{#ifeq:{{#expr:{{{1}}}>=64}}|1|*64{{#ifeq:{{#expr:{{{1}}}>=65}}|1|*65{{#ifeq:{{#expr:{{{1}}}>=66}}|1|*66{{#ifeq:{{#expr:{{{1}}}>=67}}|1|*67{{#ifeq:{{#expr:{{{1}}}>=68}}|1|*68{{#ifeq:{{#expr:{{{1}}}>=69}}|1|*69{{#ifeq:{{#expr:{{{1}}}>=70}}|1|*70{{#ifeq:{{#expr:{{{1}}}>=71}}|1|*71{{#ifeq:{{#expr:{{{1}}}>=72}}|1|*72{{#ifeq:{{#expr:{{{1}}}>=73}}|1|*73{{#ifeq:{{#expr:{{{1}}}>=74}}|1|*74{{#ifeq:{{#expr:{{{1}}}>=75}}|1|*75{{#ifeq:{{#expr:{{{1}}}>=76}}|1|*76{{#ifeq:{{#expr:{{{1}}}>=77}}|1|*77{{#ifeq:{{#expr:{{{1}}}>=78}}|1|*78{{#ifeq:{{#expr:{{{1}}}>=79}}|1|*79{{#ifeq:{{#expr:{{{1}}}>=80}}|1|*80{{#ifeq:{{#expr:{{{1}}}>=81}}|1|*81{{#ifeq:{{#expr:{{{1}}}>=82}}|1|*82{{#ifeq:{{#expr:{{{1}}}>=83}}|1|*83{{#ifeq:{{#expr:{{{1}}}>=84}}|1|*84{{#ifeq:{{#expr:{{{1}}}>=85}}|1|*85{{#ifeq:{{#expr:{{{1}}}>=86}}|1|*86{{#ifeq:{{#expr:{{{1}}}>=87}}|1|*87{{#ifeq:{{#expr:{{{1}}}>=88}}|1|*88{{#ifeq:{{#expr:{{{1}}}>=89}}|1|*89{{#ifeq:{{#expr:{{{1}}}>=90}}|1|*90{{#ifeq:{{#expr:{{{1}}}>=91}}|1|*91{{#ifeq:{{#expr:{{{1}}}>=92}}|1|*92{{#ifeq:{{#expr:{{{1}}}>=93}}|1|*93{{#ifeq:{{#expr:{{{1}}}>=94}}|1|*94{{#ifeq:{{#expr:{{{1}}}>=95}}|1|*95{{#ifeq:{{#expr:{{{1}}}>=96}}|1|*96{{#ifeq:{{#expr:{{{1}}}>=97}}|1|*97{{#ifeq:{{#expr:{{{1}}}>=98}}|1|*98{{#ifeq:{{#expr:{{{1}}}>=99}}|1|*99}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}<noinclude>
+{{Template documentation}}
+This template finds the [[factorial]] of a number. To use it, enter:<br />
+<code><nowiki>{{factorial|input}}</nowiki></code><br />
+The input must be a positive interger smaller than 100 (better than most calculators, which go up to only 69). This template works by repeating conditional multiplications. Examples:<br />
+*<nowiki>{{factorial|2}}</nowiki> gives {{factorial|2}}
+*<nowiki>{{factorial|3}}</nowiki> gives {{factorial|3}}
+*<nowiki>{{factorial|5}}</nowiki> gives {{factorial|5}}
+*<nowiki>{{factorial|10}}</nowiki> gives {{factorial|10}}
+*<nowiki>{{factorial|80}}</nowiki> gives {{factorial|80}}
+*<nowiki>{{factorial|0.5}}</nowiki> gives {{factorial|0.5}} (invalid input)
+*<nowiki>{{factorial|-1}}</nowiki> gives {{factorial|-1}} (invalid input)
+{{esoteric}}
+[[Category:Mathematical templates|{{PAGENAME}}]]
+</noinclude>
+
diff --git a/tests/parser/preprocess/Fundraising.expected b/tests/parser/preprocess/Fundraising.expected
new file mode 100644
index 00000000..f5b32cc5
--- /dev/null
+++ b/tests/parser/preprocess/Fundraising.expected
@@ -0,0 +1,18 @@
+<root>&lt;div name=&quot;fundraising&quot; id=&quot;fundraising&quot; class=&quot;plainlinks&quot; style=&quot;margin-top:5px; text-align: center; background-color: #ffffe0; border: solid 1px #e0e0c0&quot;&gt;
+'''Pwede kang [[Wikimedia:give the gift of knowledge|maghandog ng kaalaman]] sa paraan ng [[Wikimedia:Fundraising#Donation_methods|pagbibigay ng donasyon sa Pundasyong Wikimedia!]]'''
+&lt;br /&gt;
+&lt;fundraising/&gt;
+&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
+&lt;fundraisinglogo/&gt;
+&lt;br /&gt;
+&lt;b&gt;Ngayon, ang iyong [[Wikimedia:Fundraising|kontribusyon]] ay [[Wikimedia:Fundraising FAQ|itatambal]] ng isang anonimong kaibigan.&lt;/b&gt;
+&lt;br /&gt;
+&lt;small&gt;
+[[Wikimedia:Deductibility of donations|Pagbabawas sa mga buwis ng donasyon]]
+|
+[[Wikimedia:Fundraising FAQ|FAQ]]
+|
+[http://upload.wikimedia.org/wikipedia/foundation/2/28/Wikimedia_2006_fs.pdf Mga pampananalaping pahayag]
+&lt;/small&gt;
+&lt;/div&gt;
+</root> \ No newline at end of file
diff --git a/tests/parser/preprocess/Fundraising.txt b/tests/parser/preprocess/Fundraising.txt
new file mode 100644
index 00000000..b868b4d8
--- /dev/null
+++ b/tests/parser/preprocess/Fundraising.txt
@@ -0,0 +1,17 @@
+<div name="fundraising" id="fundraising" class="plainlinks" style="margin-top:5px; text-align: center; background-color: #ffffe0; border: solid 1px #e0e0c0">
+'''Pwede kang [[Wikimedia:give the gift of knowledge|maghandog ng kaalaman]] sa paraan ng [[Wikimedia:Fundraising#Donation_methods|pagbibigay ng donasyon sa Pundasyong Wikimedia!]]'''
+<br />
+<fundraising/>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+<fundraisinglogo/>
+<br />
+<b>Ngayon, ang iyong [[Wikimedia:Fundraising|kontribusyon]] ay [[Wikimedia:Fundraising FAQ|itatambal]] ng isang anonimong kaibigan.</b>
+<br />
+<small>
+[[Wikimedia:Deductibility of donations|Pagbabawas sa mga buwis ng donasyon]]
+|
+[[Wikimedia:Fundraising FAQ|FAQ]]
+|
+[http://upload.wikimedia.org/wikipedia/foundation/2/28/Wikimedia_2006_fs.pdf Mga pampananalaping pahayag]
+</small>
+</div>
diff --git a/tests/parser/preprocess/QuoteQuran.expected b/tests/parser/preprocess/QuoteQuran.expected
new file mode 100644
index 00000000..e9a78e46
--- /dev/null
+++ b/tests/parser/preprocess/QuoteQuran.expected
@@ -0,0 +1,140 @@
+<root><ignore>&lt;noinclude&gt;</ignore><template><title>Template sandbox notice</title></template><ignore>&lt;/noinclude&gt;</ignore>
+&lt;div class=&quot;boilerplate metadata rfa&quot; style=&quot;background-color:#FFFFF5; margin: 2em 0 0 0; padding: 0 10px 0 10px; border: 1px solid #AAAAAA;&quot;&gt;The [[Qur'an]], [[sura|chapter]] <template><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 1) </title><part><name index="1" /><value> 1 ([[Al-Fatiha]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 2) </title><part><name index="1" /><value> 2 ([[Al-Baqara]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 3) </title><part><name index="1" /><value> 3 ([[Ali Imran]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 4) </title><part><name index="1" /><value> 4 ([[An-Nisa]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 5) </title><part><name index="1" /><value> 5 ([[Al-Ma'ida]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 6) </title><part><name index="1" /><value> 6 ([[Al-An'am]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 7) </title><part><name index="1" /><value> 7 ([[Al-A'raf]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 8) </title><part><name index="1" /><value> 8 ([[Al-Anfal]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 9) </title><part><name index="1" /><value> 9 ([[At-Tawba]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 10) </title><part><name index="1" /><value> 10 ([[Yunus (sura)|Yunus]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 11) </title><part><name index="1" /><value> 11 ([[Hud (sura)|Hud]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 12) </title><part><name index="1" /><value> 12 ([[Yusuf (sura)|Yusuf]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 13) </title><part><name index="1" /><value> 13 ([[Ar-Ra'd]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 14) </title><part><name index="1" /><value> 14 ([[Ibrahim (sura)|Ibrahim]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 15) </title><part><name index="1" /><value> 15 ([[Al-Hijr]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 16) </title><part><name index="1" /><value> 16 ([[An-Nahl]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 17) </title><part><name index="1" /><value> 17 ([[Al-Isra]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 18) </title><part><name index="1" /><value> 18 ([[Al-Kahf]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 19) </title><part><name index="1" /><value> 19 ([[Maryam (sura)|Maryam]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 20) </title><part><name index="1" /><value> 20 ([[Ta-Ha]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 21) </title><part><name index="1" /><value> 21 ([[Al-Anbiya]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 22) </title><part><name index="1" /><value> 22 ([[Al-Hajj]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 23) </title><part><name index="1" /><value> 23 ([[Al-Muminun]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 24) </title><part><name index="1" /><value> 24 ([[An-Noor]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 25) </title><part><name index="1" /><value> 25 ([[Al-Furqan]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 26) </title><part><name index="1" /><value> 26 ([[Ash-Shu'ara]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 27) </title><part><name index="1" /><value> 27 ([[An-Naml]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 28) </title><part><name index="1" /><value> 28 ([[Al-Qisas]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 29) </title><part><name index="1" /><value> 29 ([[Al-Ankabut]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 30) </title><part><name index="1" /><value> 30 ([[Ar-Rum]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 31) </title><part><name index="1" /><value> 31 ([[Luqman (sura)|Luqman]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 32) </title><part><name index="1" /><value> 32 ([[As-Sajda]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 33) </title><part><name index="1" /><value> 33 ([[Al-Ahzab]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 34) </title><part><name index="1" /><value> 34 ([[Saba (sura)|Saba]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 35) </title><part><name index="1" /><value> 35 ([[Fatir]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 36) </title><part><name index="1" /><value> 36 ([[Ya-Seen]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 37) </title><part><name index="1" /><value> 37 ([[As-Saaffat]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 38) </title><part><name index="1" /><value> 38 ([[Sad (sura)|Sad]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 39) </title><part><name index="1" /><value> 39 ([[Az-Zumar]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 40) </title><part><name index="1" /><value> 40 ([[Ghafir]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 41) </title><part><name index="1" /><value> 41 ([[Fussilat]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 42) </title><part><name index="1" /><value> 42 ([[Ash-Shura]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 43) </title><part><name index="1" /><value> 43 ([[Az-Zukhruf]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 44) </title><part><name index="1" /><value> 44 ([[Ad-Dukhan]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 45) </title><part><name index="1" /><value> 45 ([[Al-Jathiya]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 46) </title><part><name index="1" /><value> 46 ([[Al-Ahqaf]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 47) </title><part><name index="1" /><value> 47 ([[Muhammad (sura)|Muhammad]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 48) </title><part><name index="1" /><value> 48 ([[Al-Fath]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 49) </title><part><name index="1" /><value> 49 ([[Al-Hujraat]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 50) </title><part><name index="1" /><value> 50 ([[Qaf (sura)|Qaf]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 51) </title><part><name index="1" /><value> 51 ([[Adh-Dhariyat]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 52) </title><part><name index="1" /><value> 52 ([[At-Tur]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 53) </title><part><name index="1" /><value> 53 ([[An-Najm]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 54) </title><part><name index="1" /><value> 54 ([[Al-Qamar]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 55) </title><part><name index="1" /><value> 55 ([[Ar-Rahman]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 56) </title><part><name index="1" /><value> 56 ([[Al-Waqia]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 57) </title><part><name index="1" /><value> 57 ([[Al-Hadid]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 58) </title><part><name index="1" /><value> 58 ([[Al-Mujadila]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 59) </title><part><name index="1" /><value> 59 ([[Al-Hashr]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 60) </title><part><name index="1" /><value> 60 ([[Al-Mumtahina]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 61) </title><part><name index="1" /><value> 61 ([[As-Saff]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 62) </title><part><name index="1" /><value> 62 ([[Al-Jumua]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 63) </title><part><name index="1" /><value> 63 ([[Al-Munafiqoon]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 64) </title><part><name index="1" /><value> 64 ([[At-Taghabun]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 65) </title><part><name index="1" /><value> 65 ([[At-Talaq]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 66) </title><part><name index="1" /><value> 66 ([[At-Tahrim]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 67) </title><part><name index="1" /><value> 67 ([[Al-Mulk]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 68) </title><part><name index="1" /><value> 68 ([[Al-Qalam]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 69) </title><part><name index="1" /><value> 69 ([[Al-Haaqqa]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 70) </title><part><name index="1" /><value> 70 ([[Al-Maarij]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 71) </title><part><name index="1" /><value> 71 ([[Nooh (sura)|Nooh]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 72) </title><part><name index="1" /><value> 72 ([[Al-Jinn]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 73) </title><part><name index="1" /><value> 73 ([[Al-Muzzammil]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 74) </title><part><name index="1" /><value> 74 ([[Al-Muddaththir]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 75) </title><part><name index="1" /><value> 75 ([[Al-Qiyama]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 76) </title><part><name index="1" /><value> 76 ([[Al-Insan]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 77) </title><part><name index="1" /><value> 77 ([[Al-Mursalat]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 78) </title><part><name index="1" /><value> 78 ([[An-Naba]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 79) </title><part><name index="1" /><value> 79 ([[An-Naziat]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 80) </title><part><name index="1" /><value> 80 ([[Abasa]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 81) </title><part><name index="1" /><value> 81 ([[At-Takwir]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 82) </title><part><name index="1" /><value> 82 ([[Al-Infitar]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 83) </title><part><name index="1" /><value> 83 ([[Al-Mutaffifin]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 84) </title><part><name index="1" /><value> 84 ([[Al-Inshiqaq]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 85) </title><part><name index="1" /><value> 85 ([[Al-Burooj]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 86) </title><part><name index="1" /><value> 86 ([[At-Tariq]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 87) </title><part><name index="1" /><value> 87 ([[Al-Ala]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 88) </title><part><name index="1" /><value> 88 ([[Al-Ghashiya]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 89) </title><part><name index="1" /><value> 89 ([[Al-Fajr (sura)|Al-Fajr]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 90) </title><part><name index="1" /><value> 90 ([[Al-Balad]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 91) </title><part><name index="1" /><value> 91 ([[Ash-Shams]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 92) </title><part><name index="1" /><value> 92 ([[Al-Lail]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 93) </title><part><name index="1" /><value> 93 ([[Ad-Dhuha]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 94) </title><part><name index="1" /><value> 94 ([[Al-Inshirah]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 95) </title><part><name index="1" /><value> 95 ([[At-Tin]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 96) </title><part><name index="1" /><value> 96 ([[Al-Alaq]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 97) </title><part><name index="1" /><value> 97 ([[Al-Qadr]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 98) </title><part><name index="1" /><value> 98 ([[Al-Bayyina]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 99) </title><part><name index="1" /><value> 99 ([[Az-Zalzala]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 100) </title><part><name index="1" /><value> 100 ([[Al-Adiyat]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 101) </title><part><name index="1" /><value> 101 ([[Al-Qaria]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 102) </title><part><name index="1" /><value> 102 ([[At-Takathur]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 103) </title><part><name index="1" /><value> 103 ([[Al-Asr]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 104) </title><part><name index="1" /><value> 104 ([[Al-Humaza]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 105) </title><part><name index="1" /><value> 105 ([[Al-Fil]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 106) </title><part><name index="1" /><value> 106 ([[Quraysh (sura)|Quraysh]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 107) </title><part><name index="1" /><value> 107 ([[Al-Ma'un]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 108) </title><part><name index="1" /><value> 108 ([[Al-Kawthar]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 109) </title><part><name index="1" /><value> 109 ([[Al-Kafirun]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 110) </title><part><name index="1" /><value> 110 ([[An-Nasr]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 111) </title><part><name index="1" /><value> 111 ([[Al-Masadd]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 112) </title><part><name index="1" /><value> 112 ([[Al-Ikhlas]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 113) </title><part><name index="1" /><value> 113 ([[Al-Falaq]]) </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg> = 114) </title><part><name index="1" /><value> 114 ([[An-Nas]]) </value></part><part><name index="2" /><value>
+error </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template>, [[ayat|verse]] [http://www.usc.edu/dept/MSA/quran/<template><title>three digit</title><part><name index="1" /><value><tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg></value></part></template>.qmt.html#<template><title>three digit</title><part><name index="1" /><value><tplarg><title>1</title><part><name index="1" /><value>1</value></part></tplarg></value></part></template>.<template><title>three digit</title><part><name index="1" /><value><tplarg><title>2</title><part><name index="1" /><value>1</value></part></tplarg></value></part></template> <tplarg><title>2</title><part><name index="1" /><value>1</value></part></tplarg>]''':'''<template><title>cquote</title><part><name index="1" /><value> <tplarg><title>3</title><part><name index="1" /><value>Default text</value></part></tplarg>&amp;mdash; &lt;small&gt;[[Qur'an translations|translated]] by <template><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 0) </title><part><name index="1" /><value> Unknown </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 1) </title><part><name index="1" /><value> [[Salman the Persian]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 101) </title><part><name index="1" /><value> [[Marmaduke Pickthall]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 102) </title><part><name index="1" /><value> [[Abdullah Yusuf Ali]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 601) </title><part><name index="1" /><value> [[Muhammad Muhsin Khan]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 701) </title><part><name index="1" /><value> [[Mohammed Habib Shakir|M. H. Shakir]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 901) </title><part><name index="1" /><value> [[Maulana Muhammad Ali]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 902) </title><part><name index="1" /><value> [[Rashad Khalifa]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 1001) </title><part><name index="1" /><value> [[Theodor Bibliander]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 1002) </title><part><name index="1" /><value> [[Robert of Ketton]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 1003) </title><part><name index="1" /><value> [[Andre du Ryer]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 1004) </title><part><name index="1" /><value> [[Alexander Ross (writer)|Alexander Ross]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 1005) </title><part><name index="1" /><value> [[Abraham Hinckelmann]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 1006) </title><part><name index="1" /><value> [[George Sale]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 1007) </title><part><name index="1" /><value> [[John Medows Rodwell]] </value></part><part><name index="2" /><value>
+<template lineStart="1"><title>#ifexpr: (<tplarg><title>4</title><part><name index="1" /><value>0</value></part></tplarg> = 1008) </title><part><name index="1" /><value> [[Arthur John Arberry]] </value></part><part><name index="2" /><value>
+error </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template> </value></part></template>&lt;/small&gt;
+<template lineStart="1"><title>#if:<tplarg><title>trans</title><part><name index="1" /><value></value></part></tplarg></title><part><name index="1" /><value>
+----
+[[Transliteration]]: <tplarg><title>trans</title></tplarg></value></part><part><name index="2" /><value> </value></part></template>
+<template lineStart="1"><title>#if:<tplarg><title>arab</title><part><name index="1" /><value></value></part></tplarg></title><part><name index="1" /><value>
+----
+[[Arabic language|Arabic]]: <tplarg><title>arab</title></tplarg></value></part><part><name index="2" /><value> </value></part></template> </value></part></template>&lt;/font&gt;&lt;/div&gt;
+
+</root> \ No newline at end of file
diff --git a/tests/parser/preprocess/QuoteQuran.txt b/tests/parser/preprocess/QuoteQuran.txt
new file mode 100644
index 00000000..3cfac5b2
--- /dev/null
+++ b/tests/parser/preprocess/QuoteQuran.txt
@@ -0,0 +1,139 @@
+<noinclude>{{Template sandbox notice}}</noinclude>
+<div class="boilerplate metadata rfa" style="background-color:#FFFFF5; margin: 2em 0 0 0; padding: 0 10px 0 10px; border: 1px solid #AAAAAA;">The [[Qur'an]], [[sura|chapter]] {{#ifexpr: ({{{1|1}}} = 1) | 1 ([[Al-Fatiha]]) |
+{{#ifexpr: ({{{1|1}}} = 2) | 2 ([[Al-Baqara]]) |
+{{#ifexpr: ({{{1|1}}} = 3) | 3 ([[Ali Imran]]) |
+{{#ifexpr: ({{{1|1}}} = 4) | 4 ([[An-Nisa]]) |
+{{#ifexpr: ({{{1|1}}} = 5) | 5 ([[Al-Ma'ida]]) |
+{{#ifexpr: ({{{1|1}}} = 6) | 6 ([[Al-An'am]]) |
+{{#ifexpr: ({{{1|1}}} = 7) | 7 ([[Al-A'raf]]) |
+{{#ifexpr: ({{{1|1}}} = 8) | 8 ([[Al-Anfal]]) |
+{{#ifexpr: ({{{1|1}}} = 9) | 9 ([[At-Tawba]]) |
+{{#ifexpr: ({{{1|1}}} = 10) | 10 ([[Yunus (sura)|Yunus]]) |
+{{#ifexpr: ({{{1|1}}} = 11) | 11 ([[Hud (sura)|Hud]]) |
+{{#ifexpr: ({{{1|1}}} = 12) | 12 ([[Yusuf (sura)|Yusuf]]) |
+{{#ifexpr: ({{{1|1}}} = 13) | 13 ([[Ar-Ra'd]]) |
+{{#ifexpr: ({{{1|1}}} = 14) | 14 ([[Ibrahim (sura)|Ibrahim]]) |
+{{#ifexpr: ({{{1|1}}} = 15) | 15 ([[Al-Hijr]]) |
+{{#ifexpr: ({{{1|1}}} = 16) | 16 ([[An-Nahl]]) |
+{{#ifexpr: ({{{1|1}}} = 17) | 17 ([[Al-Isra]]) |
+{{#ifexpr: ({{{1|1}}} = 18) | 18 ([[Al-Kahf]]) |
+{{#ifexpr: ({{{1|1}}} = 19) | 19 ([[Maryam (sura)|Maryam]]) |
+{{#ifexpr: ({{{1|1}}} = 20) | 20 ([[Ta-Ha]]) |
+{{#ifexpr: ({{{1|1}}} = 21) | 21 ([[Al-Anbiya]]) |
+{{#ifexpr: ({{{1|1}}} = 22) | 22 ([[Al-Hajj]]) |
+{{#ifexpr: ({{{1|1}}} = 23) | 23 ([[Al-Muminun]]) |
+{{#ifexpr: ({{{1|1}}} = 24) | 24 ([[An-Noor]]) |
+{{#ifexpr: ({{{1|1}}} = 25) | 25 ([[Al-Furqan]]) |
+{{#ifexpr: ({{{1|1}}} = 26) | 26 ([[Ash-Shu'ara]]) |
+{{#ifexpr: ({{{1|1}}} = 27) | 27 ([[An-Naml]]) |
+{{#ifexpr: ({{{1|1}}} = 28) | 28 ([[Al-Qisas]]) |
+{{#ifexpr: ({{{1|1}}} = 29) | 29 ([[Al-Ankabut]]) |
+{{#ifexpr: ({{{1|1}}} = 30) | 30 ([[Ar-Rum]]) |
+{{#ifexpr: ({{{1|1}}} = 31) | 31 ([[Luqman (sura)|Luqman]]) |
+{{#ifexpr: ({{{1|1}}} = 32) | 32 ([[As-Sajda]]) |
+{{#ifexpr: ({{{1|1}}} = 33) | 33 ([[Al-Ahzab]]) |
+{{#ifexpr: ({{{1|1}}} = 34) | 34 ([[Saba (sura)|Saba]]) |
+{{#ifexpr: ({{{1|1}}} = 35) | 35 ([[Fatir]]) |
+{{#ifexpr: ({{{1|1}}} = 36) | 36 ([[Ya-Seen]]) |
+{{#ifexpr: ({{{1|1}}} = 37) | 37 ([[As-Saaffat]]) |
+{{#ifexpr: ({{{1|1}}} = 38) | 38 ([[Sad (sura)|Sad]]) |
+{{#ifexpr: ({{{1|1}}} = 39) | 39 ([[Az-Zumar]]) |
+{{#ifexpr: ({{{1|1}}} = 40) | 40 ([[Ghafir]]) |
+{{#ifexpr: ({{{1|1}}} = 41) | 41 ([[Fussilat]]) |
+{{#ifexpr: ({{{1|1}}} = 42) | 42 ([[Ash-Shura]]) |
+{{#ifexpr: ({{{1|1}}} = 43) | 43 ([[Az-Zukhruf]]) |
+{{#ifexpr: ({{{1|1}}} = 44) | 44 ([[Ad-Dukhan]]) |
+{{#ifexpr: ({{{1|1}}} = 45) | 45 ([[Al-Jathiya]]) |
+{{#ifexpr: ({{{1|1}}} = 46) | 46 ([[Al-Ahqaf]]) |
+{{#ifexpr: ({{{1|1}}} = 47) | 47 ([[Muhammad (sura)|Muhammad]]) |
+{{#ifexpr: ({{{1|1}}} = 48) | 48 ([[Al-Fath]]) |
+{{#ifexpr: ({{{1|1}}} = 49) | 49 ([[Al-Hujraat]]) |
+{{#ifexpr: ({{{1|1}}} = 50) | 50 ([[Qaf (sura)|Qaf]]) |
+{{#ifexpr: ({{{1|1}}} = 51) | 51 ([[Adh-Dhariyat]]) |
+{{#ifexpr: ({{{1|1}}} = 52) | 52 ([[At-Tur]]) |
+{{#ifexpr: ({{{1|1}}} = 53) | 53 ([[An-Najm]]) |
+{{#ifexpr: ({{{1|1}}} = 54) | 54 ([[Al-Qamar]]) |
+{{#ifexpr: ({{{1|1}}} = 55) | 55 ([[Ar-Rahman]]) |
+{{#ifexpr: ({{{1|1}}} = 56) | 56 ([[Al-Waqia]]) |
+{{#ifexpr: ({{{1|1}}} = 57) | 57 ([[Al-Hadid]]) |
+{{#ifexpr: ({{{1|1}}} = 58) | 58 ([[Al-Mujadila]]) |
+{{#ifexpr: ({{{1|1}}} = 59) | 59 ([[Al-Hashr]]) |
+{{#ifexpr: ({{{1|1}}} = 60) | 60 ([[Al-Mumtahina]]) |
+{{#ifexpr: ({{{1|1}}} = 61) | 61 ([[As-Saff]]) |
+{{#ifexpr: ({{{1|1}}} = 62) | 62 ([[Al-Jumua]]) |
+{{#ifexpr: ({{{1|1}}} = 63) | 63 ([[Al-Munafiqoon]]) |
+{{#ifexpr: ({{{1|1}}} = 64) | 64 ([[At-Taghabun]]) |
+{{#ifexpr: ({{{1|1}}} = 65) | 65 ([[At-Talaq]]) |
+{{#ifexpr: ({{{1|1}}} = 66) | 66 ([[At-Tahrim]]) |
+{{#ifexpr: ({{{1|1}}} = 67) | 67 ([[Al-Mulk]]) |
+{{#ifexpr: ({{{1|1}}} = 68) | 68 ([[Al-Qalam]]) |
+{{#ifexpr: ({{{1|1}}} = 69) | 69 ([[Al-Haaqqa]]) |
+{{#ifexpr: ({{{1|1}}} = 70) | 70 ([[Al-Maarij]]) |
+{{#ifexpr: ({{{1|1}}} = 71) | 71 ([[Nooh (sura)|Nooh]]) |
+{{#ifexpr: ({{{1|1}}} = 72) | 72 ([[Al-Jinn]]) |
+{{#ifexpr: ({{{1|1}}} = 73) | 73 ([[Al-Muzzammil]]) |
+{{#ifexpr: ({{{1|1}}} = 74) | 74 ([[Al-Muddaththir]]) |
+{{#ifexpr: ({{{1|1}}} = 75) | 75 ([[Al-Qiyama]]) |
+{{#ifexpr: ({{{1|1}}} = 76) | 76 ([[Al-Insan]]) |
+{{#ifexpr: ({{{1|1}}} = 77) | 77 ([[Al-Mursalat]]) |
+{{#ifexpr: ({{{1|1}}} = 78) | 78 ([[An-Naba]]) |
+{{#ifexpr: ({{{1|1}}} = 79) | 79 ([[An-Naziat]]) |
+{{#ifexpr: ({{{1|1}}} = 80) | 80 ([[Abasa]]) |
+{{#ifexpr: ({{{1|1}}} = 81) | 81 ([[At-Takwir]]) |
+{{#ifexpr: ({{{1|1}}} = 82) | 82 ([[Al-Infitar]]) |
+{{#ifexpr: ({{{1|1}}} = 83) | 83 ([[Al-Mutaffifin]]) |
+{{#ifexpr: ({{{1|1}}} = 84) | 84 ([[Al-Inshiqaq]]) |
+{{#ifexpr: ({{{1|1}}} = 85) | 85 ([[Al-Burooj]]) |
+{{#ifexpr: ({{{1|1}}} = 86) | 86 ([[At-Tariq]]) |
+{{#ifexpr: ({{{1|1}}} = 87) | 87 ([[Al-Ala]]) |
+{{#ifexpr: ({{{1|1}}} = 88) | 88 ([[Al-Ghashiya]]) |
+{{#ifexpr: ({{{1|1}}} = 89) | 89 ([[Al-Fajr (sura)|Al-Fajr]]) |
+{{#ifexpr: ({{{1|1}}} = 90) | 90 ([[Al-Balad]]) |
+{{#ifexpr: ({{{1|1}}} = 91) | 91 ([[Ash-Shams]]) |
+{{#ifexpr: ({{{1|1}}} = 92) | 92 ([[Al-Lail]]) |
+{{#ifexpr: ({{{1|1}}} = 93) | 93 ([[Ad-Dhuha]]) |
+{{#ifexpr: ({{{1|1}}} = 94) | 94 ([[Al-Inshirah]]) |
+{{#ifexpr: ({{{1|1}}} = 95) | 95 ([[At-Tin]]) |
+{{#ifexpr: ({{{1|1}}} = 96) | 96 ([[Al-Alaq]]) |
+{{#ifexpr: ({{{1|1}}} = 97) | 97 ([[Al-Qadr]]) |
+{{#ifexpr: ({{{1|1}}} = 98) | 98 ([[Al-Bayyina]]) |
+{{#ifexpr: ({{{1|1}}} = 99) | 99 ([[Az-Zalzala]]) |
+{{#ifexpr: ({{{1|1}}} = 100) | 100 ([[Al-Adiyat]]) |
+{{#ifexpr: ({{{1|1}}} = 101) | 101 ([[Al-Qaria]]) |
+{{#ifexpr: ({{{1|1}}} = 102) | 102 ([[At-Takathur]]) |
+{{#ifexpr: ({{{1|1}}} = 103) | 103 ([[Al-Asr]]) |
+{{#ifexpr: ({{{1|1}}} = 104) | 104 ([[Al-Humaza]]) |
+{{#ifexpr: ({{{1|1}}} = 105) | 105 ([[Al-Fil]]) |
+{{#ifexpr: ({{{1|1}}} = 106) | 106 ([[Quraysh (sura)|Quraysh]]) |
+{{#ifexpr: ({{{1|1}}} = 107) | 107 ([[Al-Ma'un]]) |
+{{#ifexpr: ({{{1|1}}} = 108) | 108 ([[Al-Kawthar]]) |
+{{#ifexpr: ({{{1|1}}} = 109) | 109 ([[Al-Kafirun]]) |
+{{#ifexpr: ({{{1|1}}} = 110) | 110 ([[An-Nasr]]) |
+{{#ifexpr: ({{{1|1}}} = 111) | 111 ([[Al-Masadd]]) |
+{{#ifexpr: ({{{1|1}}} = 112) | 112 ([[Al-Ikhlas]]) |
+{{#ifexpr: ({{{1|1}}} = 113) | 113 ([[Al-Falaq]]) |
+{{#ifexpr: ({{{1|1}}} = 114) | 114 ([[An-Nas]]) |
+error }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }}, [[ayat|verse]] [http://www.usc.edu/dept/MSA/quran/{{three digit|{{{1|1}}}}}.qmt.html#{{three digit|{{{1|1}}}}}.{{three digit|{{{2|1}}}}} {{{2|1}}}]''':'''{{cquote| {{{3|Default text}}}&mdash; <small>[[Qur'an translations|translated]] by {{#ifexpr: ({{{4|0}}} = 0) | Unknown |
+{{#ifexpr: ({{{4|0}}} = 1) | [[Salman the Persian]] |
+{{#ifexpr: ({{{4|0}}} = 101) | [[Marmaduke Pickthall]] |
+{{#ifexpr: ({{{4|0}}} = 102) | [[Abdullah Yusuf Ali]] |
+{{#ifexpr: ({{{4|0}}} = 601) | [[Muhammad Muhsin Khan]] |
+{{#ifexpr: ({{{4|0}}} = 701) | [[Mohammed Habib Shakir|M. H. Shakir]] |
+{{#ifexpr: ({{{4|0}}} = 901) | [[Maulana Muhammad Ali]] |
+{{#ifexpr: ({{{4|0}}} = 902) | [[Rashad Khalifa]] |
+{{#ifexpr: ({{{4|0}}} = 1001) | [[Theodor Bibliander]] |
+{{#ifexpr: ({{{4|0}}} = 1002) | [[Robert of Ketton]] |
+{{#ifexpr: ({{{4|0}}} = 1003) | [[Andre du Ryer]] |
+{{#ifexpr: ({{{4|0}}} = 1004) | [[Alexander Ross (writer)|Alexander Ross]] |
+{{#ifexpr: ({{{4|0}}} = 1005) | [[Abraham Hinckelmann]] |
+{{#ifexpr: ({{{4|0}}} = 1006) | [[George Sale]] |
+{{#ifexpr: ({{{4|0}}} = 1007) | [[John Medows Rodwell]] |
+{{#ifexpr: ({{{4|0}}} = 1008) | [[Arthur John Arberry]] |
+error }} }} }} }} }} }} }} }} }} }} }} }} }} }} }} }}</small>
+{{#if:{{{trans|}}}|
+----
+[[Transliteration]]: {{{trans}}}| }}
+{{#if:{{{arab|}}}|
+----
+[[Arabic language|Arabic]]: {{{arab}}}| }} }}</font></div>
+
diff --git a/tests/parserTests.php b/tests/parserTests.php
new file mode 100644
index 00000000..99ea2ed4
--- /dev/null
+++ b/tests/parserTests.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * MediaWiki parser test suite
+ *
+ * Copyright © 2004 Brion Vibber <brion@pobox.com>
+ * http://www.mediawiki.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Testing
+ */
+
+$options = array( 'quick', 'color', 'quiet', 'help', 'show-output', 'record', 'run-disabled' );
+$optionsWithArgs = array( 'regex', 'seed', 'setversion' );
+
+require_once( dirname( __FILE__ ) . '/../maintenance/commandLine.inc' );
+
+if ( isset( $options['help'] ) ) {
+ echo <<<ENDS
+MediaWiki $wgVersion parser test suite
+Usage: php parserTests.php [options...]
+
+Options:
+ --quick Suppress diff output of failed tests
+ --quiet Suppress notification of passed tests (shows only failed tests)
+ --show-output Show expected and actual output
+ --color[=yes|no] Override terminal detection and force color output on or off
+ use wgCommandLineDarkBg = true; if your term is dark
+ --regex Only run tests whose descriptions which match given regex
+ --file=<testfile> Run test cases from a custom file instead of parserTests.txt
+ --record Record tests in database
+ --compare Compare with recorded results, without updating the database.
+ --setversion When using --record, set the version string to use (useful
+ with git-svn so that you can get the exact revision)
+ --keep-uploads Re-use the same upload directory for each test, don't delete it
+ --fuzz Do a fuzz test instead of a normal test
+ --seed <n> Start the fuzz test from the specified seed
+ --help Show this help message
+ --run-disabled run disabled tests
+
+ENDS;
+ exit( 0 );
+}
+
+# Cases of weird db corruption were encountered when running tests on earlyish
+# versions of SQLite
+if ( $wgDBtype == 'sqlite' ) {
+ $db = wfGetDB( DB_MASTER );
+ $version = $db->getServerVersion();
+ if ( version_compare( $version, '3.6' ) < 0 ) {
+ die( "Parser tests require SQLite version 3.6 or later, you have $version\n" );
+ }
+}
+
+# There is a convention that the parser should never
+# refer to $wgTitle directly, but instead use the title
+# passed to it.
+$wgTitle = Title::newFromText( 'Parser test script do not use' );
+$tester = new ParserTest($options);
+
+if ( isset( $options['file'] ) ) {
+ $files = array( $options['file'] );
+} else {
+ // Default parser tests and any set from extensions or local config
+ $files = $wgParserTestFiles;
+}
+
+# Print out software version to assist with locating regressions
+$version = SpecialVersion::getVersion();
+echo( "This is MediaWiki version {$version}.\n\n" );
+
+if ( isset( $options['fuzz'] ) ) {
+ $tester->fuzzTest( $files );
+} else {
+ $ok = $tester->runTestsFromFiles( $files );
+ exit ( $ok ? 0 : 1 );
+}
diff --git a/tests/phpunit/Makefile b/tests/phpunit/Makefile
new file mode 100644
index 00000000..24536efc
--- /dev/null
+++ b/tests/phpunit/Makefile
@@ -0,0 +1,82 @@
+.PHONY: help test phpunit install coverage warning destructive parser noparser safe databaseless list-groups
+.DEFAULT: warning
+
+SHELL = /bin/sh
+CONFIG_FILE = $(shell pwd)/suite.xml
+PHP = php
+PU = ${PHP} phpunit.php --configuration ${CONFIG_FILE} ${FLAGS}
+
+all test: warning
+
+warning:
+ @echo "Run 'make help' to get usage"
+ @echo ""
+ @echo "WARNING -- some tests are DESTRUCTIVE and will alter your wiki."
+ @echo "DO NOT RUN THESE TESTS on a production wiki."
+ @echo ""
+ @echo "Until the default tests are made non-destructive, you can run"
+ @echo "the destructive tests like so:"
+ @echo ""
+ @echo " make destructive"
+ @echo ""
+ @echo "Some tests are expected to be safe, you can run them with"
+ @echo ""
+ @echo " make safe"
+ @echo ""
+ @echo "You are recommended to run the tests with read-only credentials."
+ @echo ""
+ @echo "If you don't have a database running, you can still run"
+ @echo ""
+ @echo " make databaseless"
+ @echo ""
+
+destructive: phpunit
+
+phpunit:
+ ${PU}
+
+install:
+ ./install-phpunit.sh
+
+tap:
+ ${PU} --tap
+
+coverage:
+ ${PU} --coverage-html ../../docs/code-coverage
+
+parser:
+ ${PU} --group Parser
+noparser:
+ ${PU} --exclude-group Parser,Broken,Stub
+
+safe:
+ ${PU} --exclude-group Broken,Destructive,Stub
+
+databaseless:
+ ${PU} --exclude-group Broken,Destructive,Database,Stub
+
+database:
+ ${PU} --exclude-group Broken,Destructive,Stub --group Database
+
+list-groups:
+ ${PU} --list-groups
+
+help:
+ # Usage:
+ # make <target> [OPTION=value]
+ #
+ # Targets:
+ # phpunit (default) Run all the tests with phpunit
+ # install Install PHPUnit from phpunit.de
+ # tap Run the tests individually through Test::Harness's prove(1)
+ # help You're looking at it!
+ # coverage Run the tests and generates an HTML code coverage report
+ # You will need the Xdebug PHP extension for the later.
+ # [no]parser Skip or only run Parser tests
+ #
+ # list-groups List availabe Tests groups.
+ #
+ # Options:
+ # CONFIG_FILE Path to a PHPUnit configuration file (default: suite.xml)
+ # FLAGS Additional flags to pass to PHPUnit
+ # PHP Path to php
diff --git a/tests/phpunit/MediaWikiLangTestCase.php b/tests/phpunit/MediaWikiLangTestCase.php
new file mode 100644
index 00000000..1cd6a3ba
--- /dev/null
+++ b/tests/phpunit/MediaWikiLangTestCase.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * Base class that store and restore the Language objects
+ */
+abstract class MediaWikiLangTestCase extends MediaWikiTestCase {
+ private static $oldLang;
+ private static $oldContLang;
+
+ public function setUp() {
+ global $wgLanguageCode, $wgLang, $wgContLang;
+
+ self::$oldLang = $wgLang;
+ self::$oldContLang = $wgContLang;
+
+ if( $wgLanguageCode != $wgContLang->getCode() ) die("nooo!");
+
+ $wgLanguageCode = 'en'; # For mainpage to be 'Main Page'
+
+ $wgContLang = $wgLang = Language::factory( $wgLanguageCode );
+ MessageCache::singleton()->disable();
+ }
+
+ public function tearDown() {
+ global $wgContLang, $wgLang, $wgLanguageCode;
+ $wgLang = self::$oldLang;
+
+ $wgContLang = self::$oldContLang;
+ $wgLanguageCode = $wgContLang->getCode();
+ self::$oldContLang = self::$oldLang = null;
+ }
+
+}
diff --git a/tests/phpunit/MediaWikiPHPUnitCommand.php b/tests/phpunit/MediaWikiPHPUnitCommand.php
new file mode 100644
index 00000000..c0d9f363
--- /dev/null
+++ b/tests/phpunit/MediaWikiPHPUnitCommand.php
@@ -0,0 +1,46 @@
+<?php
+
+class MediaWikiPHPUnitCommand extends PHPUnit_TextUI_Command {
+
+ static $additionalOptions = array(
+ 'regex=' => false,
+ 'file=' => false,
+ 'keep-uploads' => false,
+ );
+
+ public function __construct() {
+ foreach( self::$additionalOptions as $option => $default ) {
+ $this->longOptions[$option] = $option . 'Handler';
+ }
+
+ }
+
+ public static function main( $exit = true ) {
+ $command = new self;
+ $command->run($_SERVER['argv'], $exit);
+ }
+
+ public function __call( $func, $args ) {
+
+ if( substr( $func, -7 ) == 'Handler' ) {
+ if( is_null( $args[0] ) ) $args[0] = true; //Booleans
+ self::$additionalOptions[substr( $func, 0, -7 ) ] = $args[0];
+ }
+ }
+
+ public function showHelp() {
+ parent::showHelp();
+
+ print <<<EOT
+
+ParserTest-specific options:
+
+ --regex="<regex>" Only run parser tests that match the given regex
+ --file="<filename>" Prints the version and exits.
+ --keep-uploads Re-use the same upload directory for each test, don't delete it
+
+
+EOT;
+ }
+
+}
diff --git a/tests/phpunit/MediaWikiTestCase.php b/tests/phpunit/MediaWikiTestCase.php
new file mode 100644
index 00000000..64cb486b
--- /dev/null
+++ b/tests/phpunit/MediaWikiTestCase.php
@@ -0,0 +1,239 @@
+<?php
+
+abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
+ public $suite;
+ public $regex = '';
+ public $runDisabled = false;
+
+ /**
+ * @var DatabaseBase
+ */
+ protected $db;
+ protected $oldTablePrefix;
+ protected $useTemporaryTables = true;
+ private static $dbSetup = false;
+
+ /**
+ * Table name prefixes. Oracle likes it shorter.
+ */
+ const DB_PREFIX = 'unittest_';
+ const ORA_DB_PREFIX = 'ut_';
+
+ protected $supportedDBs = array(
+ 'mysql',
+ 'sqlite',
+ 'oracle'
+ );
+
+ function __construct( $name = null, array $data = array(), $dataName = '' ) {
+ parent::__construct( $name, $data, $dataName );
+
+ $this->backupGlobals = false;
+ $this->backupStaticAttributes = false;
+ }
+
+ function run( PHPUnit_Framework_TestResult $result = NULL ) {
+ /* Some functions require some kind of caching, and will end up using the db,
+ * which we can't allow, as that would open a new connection for mysql.
+ * Replace with a HashBag. They would not be going to persist anyway.
+ */
+ ObjectCache::$instances[CACHE_DB] = new HashBagOStuff;
+
+ if( $this->needsDB() ) {
+
+ global $wgDBprefix;
+
+ $this->db = wfGetDB( DB_MASTER );
+
+ $this->checkDbIsSupported();
+
+ $this->oldTablePrefix = $wgDBprefix;
+
+ if( !self::$dbSetup ) {
+ $this->initDB();
+ self::$dbSetup = true;
+ }
+
+ $this->addCoreDBData();
+ $this->addDBData();
+
+ parent::run( $result );
+
+ $this->resetDB();
+ } else {
+ parent::run( $result );
+ }
+ }
+
+ function dbPrefix() {
+ return $this->db->getType() == 'oracle' ? self::ORA_DB_PREFIX : self::DB_PREFIX;
+ }
+
+ function needsDB() {
+ $rc = new ReflectionClass( $this );
+ return strpos( $rc->getDocComment(), '@group Database' ) !== false;
+ }
+
+ /**
+ * Stub. If a test needs to add additional data to the database, it should
+ * implement this method and do so
+ */
+ function addDBData() {}
+
+ private function addCoreDBData() {
+
+ User::resetIdByNameCache();
+
+ //Make sysop user
+ $user = User::newFromName( 'UTSysop' );
+
+ if ( $user->idForName() == 0 ) {
+ $user->addToDatabase();
+ $user->setPassword( 'UTSysopPassword' );
+
+ $user->addGroup( 'sysop' );
+ $user->addGroup( 'bureaucrat' );
+ $user->saveSettings();
+ }
+
+
+ //Make 1 page with 1 revision
+ $article = new Article( Title::newFromText( 'UTPage' ) );
+ $article->doEdit( 'UTContent',
+ 'UTPageSummary',
+ EDIT_NEW,
+ false,
+ User::newFromName( 'UTSysop' ) );
+ }
+
+ private function initDB() {
+ global $wgDBprefix;
+ if ( $wgDBprefix === $this->dbPrefix() ) {
+ throw new MWException( 'Cannot run unit tests, the database prefix is already "unittest_"' );
+ }
+
+ $dbClone = new CloneDatabase( $this->db, $this->listTables(), $this->dbPrefix() );
+ $dbClone->useTemporaryTables( $this->useTemporaryTables );
+ $dbClone->cloneTableStructure();
+
+ if ( $this->db->getType() == 'oracle' ) {
+ $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
+
+ # Insert 0 user to prevent FK violations
+ # Anonymous user
+ $this->db->insert( 'user', array(
+ 'user_id' => 0,
+ 'user_name' => 'Anonymous' ) );
+ }
+ }
+
+ /**
+ * Empty all tables so they can be repopulated for tests
+ */
+ private function resetDB() {
+ if( $this->db ) {
+ foreach( $this->listTables() as $tbl ) {
+ if( $tbl == 'interwiki' || $tbl == 'user' ) continue;
+ $this->db->delete( $tbl, '*', __METHOD__ );
+ }
+ }
+ }
+
+ protected function destroyDB() {
+ if ( $this->useTemporaryTables || is_null( $this->db ) ) {
+ # Don't need to do anything
+ return;
+ }
+
+ $tables = $this->db->listTables( $this->dbPrefix(), __METHOD__ );
+
+ foreach ( $tables as $table ) {
+ try {
+ $sql = $this->db->getType() == 'oracle' ? "DROP TABLE $table CASCADE CONSTRAINTS PURGE" : "DROP TABLE `$table`";
+ $this->db->query( $sql, __METHOD__ );
+ } catch( MWException $mwe ) {}
+ }
+
+ if ( $this->db->getType() == 'oracle' )
+ $this->db->query( 'BEGIN FILL_WIKI_INFO; END;', __METHOD__ );
+
+ CloneDatabase::changePrefix( $this->oldTablePrefix );
+ }
+
+
+ function __call( $func, $args ) {
+ static $compatibility = array(
+ 'assertInternalType' => 'assertType',
+ 'assertNotInternalType' => 'assertNotType',
+ 'assertInstanceOf' => 'assertType',
+ 'assertEmpty' => 'assertEmpty2',
+ );
+
+ if ( method_exists( $this->suite, $func ) ) {
+ return call_user_func_array( array( $this->suite, $func ), $args);
+ } elseif ( isset( $compatibility[$func] ) ) {
+ return call_user_func_array( array( $this, $compatibility[$func] ), $args);
+ } else {
+ throw new MWException( "Called non-existant $func method on "
+ . get_class( $this ) );
+ }
+ }
+
+ private function assertEmpty2( $value, $msg ) {
+ return $this->assertTrue( $value == '', $msg );
+ }
+
+ static private function unprefixTable( $tableName ) {
+ global $wgDBprefix;
+ return substr( $tableName, strlen( $wgDBprefix ) );
+ }
+
+ static private function isNotUnittest( $table ) {
+ return strpos( $table, 'unittest_' ) !== 0;
+ }
+
+ protected function listTables() {
+ global $wgDBprefix;
+
+ $tables = $this->db->listTables( $wgDBprefix, __METHOD__ );
+ $tables = array_map( array( __CLASS__, 'unprefixTable' ), $tables );
+
+ // Don't duplicate test tables from the previous fataled run
+ $tables = array_filter( $tables, array( __CLASS__, 'isNotUnittest' ) );
+
+ if ( $this->db->getType() == 'sqlite' ) {
+ $tables = array_flip( $tables );
+ // these are subtables of searchindex and don't need to be duped/dropped separately
+ unset( $tables['searchindex_content'] );
+ unset( $tables['searchindex_segdir'] );
+ unset( $tables['searchindex_segments'] );
+ $tables = array_flip( $tables );
+ }
+ return $tables;
+ }
+
+ protected function checkDbIsSupported() {
+ if( !in_array( $this->db->getType(), $this->supportedDBs ) ) {
+ throw new MWException( $this->db->getType() . " is not currently supported for unit testing." );
+ }
+ }
+
+ public function getCliArg( $offset ) {
+
+ if( isset( MediaWikiPHPUnitCommand::$additionalOptions[$offset] ) ) {
+ return MediaWikiPHPUnitCommand::$additionalOptions[$offset];
+ }
+
+ }
+
+ public function setCliArg( $offset, $value ) {
+
+ MediaWikiPHPUnitCommand::$additionalOptions[$offset] = $value;
+
+ }
+
+ public static function disableInterwikis( $prefix, &$data ) {
+ return false;
+ }
+}
+
diff --git a/tests/phpunit/README b/tests/phpunit/README
new file mode 100644
index 00000000..0a32ba17
--- /dev/null
+++ b/tests/phpunit/README
@@ -0,0 +1,53 @@
+== MediaWiki PHPUnit Tests ==
+
+The unit tests for MediaWiki are implemented using the PHPUnit testing
+framework and require PHPUnit to run.
+
+
+=== WARNING ===
+
+Some of the unit tests are DESTRUCTIVE and WILL ALTER YOUR WIKI'S CONTENTS.
+
+DO NOT RUN THESE TESTS ON A PRODUCTION SYSTEM OR ON ANY SYSTEM WHERE YOU NEED
+TO RETAIN YOUR DATA.
+
+
+== Installation ==
+
+If PHPUnit is not installed, follow the installation instructions in the
+PHPUnit Manual at:
+
+ http://www.phpunit.de/manual/current/en/installation.html
+
+- or -
+
+On Unix-like operating systems, run:
+
+ make install
+
+
+== Running tests ==
+
+The tests are run from your operating system's command line.
+
+Ensure that you are in the tests/phpunit directory of your MediaWiki
+installation.
+
+
+On Unix-like operating systems, the tests runs are controlled with a makefile.
+Run command:
+
+ make help
+
+for a full list of options for running tests.
+
+
+On Windows-family operating systems, run the 'run-tests.bat' batch file.
+
+
+=== Writing tests ===
+
+A guide to writing unit tests for MediaWiki can be found at:
+
+ http://mediawiki.org/wiki/Unit_Testing
+
diff --git a/tests/phpunit/TODO b/tests/phpunit/TODO
new file mode 100644
index 00000000..b2fa7fb6
--- /dev/null
+++ b/tests/phpunit/TODO
@@ -0,0 +1,10 @@
+== Things To Do ==
+* Most of the tests are named poorly; naming should describe a use case in story-like language, not simply identify the
+unit under test. An example would be the difference between testCalculate and testAddingIntegersTogetherWorks.
+* Many of the tests make multiple assertions, and are thus not unitary tests. By using data-providers and more use-case
+oriented test selection nearly all of these cases can be easily resolved.
+* Some of the test files are either incorrectly named or in the wrong folder. Tests should be organized in a mirrored
+structure to the source they are testing, and named the same, with the exception of the word "Test" at the end.
+* Shared set-up code or base classes are present, but usually named improperly or appear to be poorly factored. Support
+code should share as much of the same naming as the code it's supporting, and test and test-case depenencies should be
+considered to resolve other shared needs.
diff --git a/tests/phpunit/bootstrap.php b/tests/phpunit/bootstrap.php
new file mode 100644
index 00000000..b023fdcf
--- /dev/null
+++ b/tests/phpunit/bootstrap.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Bootstrapping for MediaWiki PHPUnit tests
+ * This file is included by phpunit and is NOT in the global scope.
+ *
+ * @file
+ */
+
+if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
+ echo <<<EOF
+You are running these tests directly from phpunit. You may not have all globals correctly set.
+Running phpunit.php instead is recommended.
+EOF;
+ require_once ( dirname( __FILE__ ) . "/phpunit.php" );
+}
+
+// Output a notice when running with older versions of PHPUnit
+if ( !version_compare( PHPUnit_Runner_Version::id(), "3.4.1", ">" ) ) {
+ echo <<<EOF
+********************************************************************************
+
+These tests run best with version PHPUnit 3.4.2 or better. Earlier versions may
+show failures because earlier versions of PHPUnit do not properly implement
+dependencies.
+
+********************************************************************************
+
+EOF;
+}
+
+/** @todo Check if this is really needed */
+MessageCache::destroyInstance();
diff --git a/tests/phpunit/data/media/1bit-png.png b/tests/phpunit/data/media/1bit-png.png
new file mode 100644
index 00000000..254e403a
--- /dev/null
+++ b/tests/phpunit/data/media/1bit-png.png
Binary files differ
diff --git a/tests/phpunit/data/media/Animated_PNG_example_bouncing_beach_ball.png b/tests/phpunit/data/media/Animated_PNG_example_bouncing_beach_ball.png
new file mode 100644
index 00000000..c2f45d90
--- /dev/null
+++ b/tests/phpunit/data/media/Animated_PNG_example_bouncing_beach_ball.png
Binary files differ
diff --git a/tests/phpunit/data/media/Gtk-media-play-ltr.svg b/tests/phpunit/data/media/Gtk-media-play-ltr.svg
new file mode 100644
index 00000000..fc22338a
--- /dev/null
+++ b/tests/phpunit/data/media/Gtk-media-play-ltr.svg
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" [
+ <!ATTLIST svg
+ xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
+]>
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" version="1" x="0.00000000" y="0.00000000" width="60.0000000" height="60.0000000" viewBox="0 0 256 256" id="svg548">
+ <defs id="defs572"/>
+ <g style="font-size:12;stroke:#000000;" id="Layer_1">
+ <path d="M 256 256 L 0 256 L 0 0 L 256 0 L 256 256 z " style="fill:none;stroke:none;" id="path550"/>
+ </g>
+ <g style="font-size:12;stroke:#000000;" id="Layer_2">
+ <path d="M 35.159 8.29 C 32.18 10.01 30.329 13.216 30.329 16.656 L 30.329 245.539 C 30.329 248.978 32.179 252.184 35.158 253.902 C 38.138 255.623 41.839 255.623 44.817 253.904 L 243.037 139.463 C 246.016 137.742 247.867 134.537 247.867 131.098 C 247.867 127.658 246.016 124.452 243.037 122.731 L 44.818 8.29 C 41.839 6.57 38.138 6.57 35.159 8.29 z " style="opacity:0.2;stroke:none;" id="path552"/>
+ <path d="M 27.314 2.29 C 24.335 4.01 22.484 7.216 22.484 10.656 L 22.484 239.538 C 22.484 242.977 24.335 246.184 27.313 247.903 C 30.293 249.623 33.994 249.623 36.973 247.905 L 235.193 133.464 C 238.172 131.742 240.023 128.536 240.023 125.098 C 240.023 121.658 238.172 118.452 235.193 116.732 L 36.975 2.29 C 33.996 0.57 30.294 0.57 27.314 2.29 z " style="fill:#003399;stroke:none;" id="path553"/>
+ <path d="M 29.247 5.636 C 27.454 6.672 26.349 8.585 26.349 10.656 L 26.349 239.538 C 26.349 241.608 27.453 243.521 29.247 244.558 C 31.04 245.592 33.249 245.592 35.042 244.558 L 233.261 130.117 C 235.054 129.081 236.159 127.169 236.159 125.098 C 236.159 123.027 235.054 121.114 233.261 120.078 L 35.042 5.636 C 33.25 4.601 31.041 4.601 29.247 5.636 z " style="fill:#003399;stroke:none;" id="path554"/>
+ <path d="M 32.145 10.656 L 230.364 125.097 L 32.145 239.538 L 32.145 10.656 z " style="fill:#3399ff;stroke:none;" id="path555"/>
+ <linearGradient x1="109.971703" y1="8.70849991" x2="109.971703" y2="107.238800" id="XMLID_1_" gradientUnits="userSpaceOnUse" spreadMethod="pad">
+ <stop style="stop-color:#ffffff;stop-opacity:1;" offset="0.00000000" id="stop557"/>
+ <stop style="stop-color:#3399ff;stop-opacity:1;" offset="1.00000000" id="stop558"/>
+ <a:midPointStop offset="0" style="stop-color:#FFFFFF" id="midPointStop559"/>
+ <a:midPointStop offset="0.5" style="stop-color:#FFFFFF" id="midPointStop560"/>
+ <a:midPointStop offset="1" style="stop-color:#3399FF" id="midPointStop561"/>
+ </linearGradient>
+ <path d="M 32.145 141.057 C 36.775 141.258 41.456 141.368 46.183 141.368 C 105.41 141.368 157.526 125.124 187.799 100.524 L 32.145 10.656 L 32.145 141.057 z " style="fill:url(#XMLID_1_);stroke:none;" id="path562"/>
+ <linearGradient x1="109.972198" y1="264.875000" x2="109.972198" y2="145.249298" id="XMLID_2_" gradientUnits="userSpaceOnUse" spreadMethod="pad">
+ <stop style="stop-color:#ccffff;stop-opacity:1;" offset="0.00000000" id="stop564"/>
+ <stop style="stop-color:#3399ff;stop-opacity:1;" offset="1.00000000" id="stop565"/>
+ <a:midPointStop offset="0" style="stop-color:#CCFFFF" id="midPointStop566"/>
+ <a:midPointStop offset="0.5" style="stop-color:#CCFFFF" id="midPointStop567"/>
+ <a:midPointStop offset="1" style="stop-color:#3399FF" id="midPointStop568"/>
+ </linearGradient>
+ <path d="M 32.145 108.517 C 36.775 108.315 41.456 108.206 46.183 108.206 C 105.41 108.206 157.526 124.451 187.799 149.05 L 32.145 238.916 L 32.145 108.517 z " style="fill:url(#XMLID_2_);stroke:none;" id="path569"/>
+ <path d="M 37.145 19.316 C 36.526 19.673 36.145 20.334 36.145 21.048 L 36.145 162.69 C 36.145 163.768 36.999 198.629 38.077 198.667 C 39.154 198.703 40.41 48.03 48.066 40.375 C 55.722 32.72 212.492 122.951 213 122 C 213.507 121.049 186.703 104.509 185.77 103.97 L 39.145 19.316 C 38.526 18.959 37.764 18.959 37.145 19.316 z " style="opacity:0.5;fill:#ffffff;stroke:none;" id="path570"/>
+ </g>
+</svg> \ No newline at end of file
diff --git a/tests/phpunit/data/media/Png-native-test.png b/tests/phpunit/data/media/Png-native-test.png
new file mode 100644
index 00000000..a0b81ca9
--- /dev/null
+++ b/tests/phpunit/data/media/Png-native-test.png
Binary files differ
diff --git a/tests/phpunit/data/media/QA_icon.svg b/tests/phpunit/data/media/QA_icon.svg
new file mode 100644
index 00000000..6b5d86e4
--- /dev/null
+++ b/tests/phpunit/data/media/QA_icon.svg
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg:svg xmlns:svg="http://www.w3.org/2000/svg" version="1.0" width="60" height="60" viewBox="0 0 128 128" id="svg548">
+ <svg:defs id="defs601">
+ <svg:linearGradient id="linearGradient2802">
+ <svg:stop style="stop-color:#1d12aa;stop-opacity:1" offset="0" id="stop2804"/>
+ <svg:stop style="stop-color:#8b12aa;stop-opacity:0" offset="1" id="stop2806"/>
+ </svg:linearGradient>
+ <svg:linearGradient id="linearGradient2812">
+ <svg:stop style="stop-color:#1d25aa;stop-opacity:1" offset="0" id="stop2814"/>
+ <svg:stop style="stop-color:#8b12aa;stop-opacity:0" offset="1" id="stop2816"/>
+ </svg:linearGradient>
+ <svg:marker refX="0" refY="0" orient="auto" style="overflow:visible" id="Arrow1Lstart">
+ <svg:path d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z " transform="scale(0.8)" style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" id="path2991"/>
+ </svg:marker>
+ <svg:linearGradient id="linearGradient4766">
+ <svg:stop style="stop-color:#0447ff;stop-opacity:1" offset="0" id="stop4768"/>
+ <svg:stop style="stop-color:#000000;stop-opacity:0" offset="1" id="stop4770"/>
+ </svg:linearGradient>
+ <svg:linearGradient x1="55.4272" y1="102.1953" x2="55.4272" y2="-7.1773" id="XMLID_1_" gradientUnits="userSpaceOnUse" gradientTransform="translate(0, -0.496766)" spreadMethod="pad">
+ <svg:stop style="stop-color:#7c74ff;stop-opacity:1" offset="0" id="stop556"/>
+ <svg:stop style="stop-color:#b3caff;stop-opacity:1" offset="0.41010001" id="stop557"/>
+ <svg:stop style="stop-color:#dfeaff;stop-opacity:1" offset="0.8258" id="stop558"/>
+ <svg:stop style="stop-color:#ffffff;stop-opacity:1" offset="1" id="stop559"/>
+ <midPointStop offset="0" style="stop-color:#7C74FF" id="midPointStop560"/>
+ <midPointStop offset="0.5" style="stop-color:#7C74FF" id="midPointStop561"/>
+ <midPointStop offset="0.4101" style="stop-color:#B3CAFF" id="midPointStop562"/>
+ <midPointStop offset="0.5" style="stop-color:#B3CAFF" id="midPointStop563"/>
+ <midPointStop offset="0.8258" style="stop-color:#DFEAFF" id="midPointStop564"/>
+ <midPointStop offset="0.5" style="stop-color:#DFEAFF" id="midPointStop565"/>
+ <midPointStop offset="1" style="stop-color:#FFFFFF" id="midPointStop566"/>
+ </svg:linearGradient>
+ <svg:linearGradient x1="54.7607" y1="7.2758999" x2="54.7607" y2="57.487301" id="XMLID_2_" gradientUnits="userSpaceOnUse" spreadMethod="pad">
+ <svg:stop style="stop-color:#ffffff;stop-opacity:1" offset="0" id="stop569"/>
+ <svg:stop style="stop-color:#b3caff;stop-opacity:1" offset="1" id="stop570"/>
+ <midPointStop offset="0" style="stop-color:#FFFFFF" id="midPointStop571"/>
+ <midPointStop offset="0.5" style="stop-color:#FFFFFF" id="midPointStop572"/>
+ <midPointStop offset="1" style="stop-color:#B3CAFF" id="midPointStop573"/>
+ </svg:linearGradient>
+ <svg:linearGradient x1="83.637703" y1="119.3457" x2="83.637703" y2="42.033901" id="XMLID_3_" gradientUnits="userSpaceOnUse" spreadMethod="pad">
+ <svg:stop style="stop-color:#006dff;stop-opacity:1" offset="0" id="stop577"/>
+ <svg:stop style="stop-color:#94caff;stop-opacity:1" offset="0.41010001" id="stop578"/>
+ <svg:stop style="stop-color:#dcf0ff;stop-opacity:1" offset="0.8258" id="stop579"/>
+ <svg:stop style="stop-color:#ffffff;stop-opacity:1" offset="1" id="stop580"/>
+ <midPointStop offset="0" style="stop-color:#006DFF" id="midPointStop581"/>
+ <midPointStop offset="0.5" style="stop-color:#006DFF" id="midPointStop582"/>
+ <midPointStop offset="0.4101" style="stop-color:#94CAFF" id="midPointStop583"/>
+ <midPointStop offset="0.5" style="stop-color:#94CAFF" id="midPointStop584"/>
+ <midPointStop offset="0.8258" style="stop-color:#DCF0FF" id="midPointStop585"/>
+ <midPointStop offset="0.5" style="stop-color:#DCF0FF" id="midPointStop586"/>
+ <midPointStop offset="1" style="stop-color:#FFFFFF" id="midPointStop587"/>
+ </svg:linearGradient>
+ <svg:linearGradient x1="265.11331" y1="52.250999" x2="265.11331" y2="87.743599" id="XMLID_4_" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1, 0, 0, 1, 349, 0)" spreadMethod="pad">
+ <svg:stop style="stop-color:#ffffff;stop-opacity:1" offset="0" id="stop590"/>
+ <svg:stop style="stop-color:#94caff;stop-opacity:1" offset="1" id="stop591"/>
+ <midPointStop offset="0" style="stop-color:#FFFFFF" id="midPointStop592"/>
+ <midPointStop offset="0.5" style="stop-color:#FFFFFF" id="midPointStop593"/>
+ <midPointStop offset="1" style="stop-color:#94CAFF" id="midPointStop594"/>
+ </svg:linearGradient>
+ </svg:defs>
+ <svg:g style="font-size:12px;stroke:#000000" id="Layer_2">
+ <svg:path d="M 128,128 L 0,128 L 0,0 L 128,0 L 128,128 z " style="fill:none;stroke:none" id="path550"/>
+ </svg:g>
+ <svg:g style="font-size:12px;stroke:#000000" id="Layer_1"/>
+ <svg:path d="M 9.041,92.189 C 9.041,92.189 21.955,85.393 30.11,67.382 L 52.198,76.897 C 52.198,76.897 46.422,92.189 9.041,92.189 z " style="font-size:12px;fill:#00008d;stroke:none" id="path553"/>
+ <svg:path d="M 1.905,49.712 C 1.905,70.733 25.867,87.773 55.427,87.773 C 84.987,87.773 108.949,70.733 108.949,49.712 C 108.949,28.692 84.987,11.651 55.427,11.651 C 25.867,11.651 1.905,28.692 1.905,49.712 z " style="font-size:12px;fill:#00008d;stroke:none" id="path554"/>
+ <svg:path d="M 55.427,13.193234 C 27.039,13.193234 3.943,29.352234 3.943,49.214234 C 3.943,61.333234 12.55,72.067234 25.703,78.598234 C 22.202,83.521234 18.6,87.075234 15.722,89.464234 C 27.71,88.800234 35.664,86.388234 40.883,83.762234 C 45.498,84.716234 50.377,85.236234 55.427,85.236234 C 83.815,85.236234 106.91,69.077234 106.91,49.215234 C 106.91,29.353234 83.815,13.193234 55.427,13.193234 z " style="font-size:12px;fill:url(#XMLID_1_);stroke:none" id="path567"/>
+ <svg:path d="M 12.999,35.282 C 30.044,44.81 49.474,47.149 69.356,41.962 C 73.46,40.821 77.627,39.436 81.656,38.096 C 86.51,36.482 91.504,34.846 96.524,33.573 C 88.559,23.302 72.888,16.748 55.428,16.748 C 37.091,16.749 20.396,24.128 12.999,35.282 z " style="font-size:12px;fill:url(#XMLID_2_);stroke:none" id="path574"/>
+ <svg:text x="32.487015" y="68.006958" style="font-size:48px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Microsoft Sans Serif" id="text2303" xml:space="preserve"><svg:tspan x="32.487015" y="68.006958" style="font-size:64px;font-style:normal;font-weight:bold;fill:#000000;fill-opacity:1;font-family:sans" id="tspan2305">?</svg:tspan></svg:text>
+ <svg:path d="M 42.401,82.248 C 42.401,98.96 60.9,112.557 83.638,112.557 C 86.794,112.557 90.053,112.231 93.329,111.651 C 98.255,113.817 104.317,115.142 111.437,115.538 L 126.095,116.35 L 114.798,106.972 C 114.067,106.367 113.056,105.441 111.922,104.237 C 120.165,98.531 124.875,90.66 124.875,82.25 C 124.875,65.538 106.376,51.942 83.637,51.942 C 60.9,51.94 42.401,65.536 42.401,82.248 z " style="font-size:12px;fill:#0032a4;stroke:none" id="path575"/>
+ <svg:path d="M 44.823,82.248 C 44.823,97.624 62.236,110.133 83.637,110.133 C 87.009,110.133 90.357,109.784 93.616,109.163 C 98.327,111.368 104.334,112.717 111.571,113.118 L 118.9,113.524 L 113.251,108.835 C 111.96,107.763 110.162,106.071 108.268,103.754 C 117.176,98.487 122.454,90.629 122.454,82.248 C 122.454,66.871 105.042,54.363 83.639,54.363 C 62.236,54.363 44.823,66.871 44.823,82.248 z " style="font-size:12px;fill:url(#XMLID_3_);stroke:none" id="path588"/>
+ <svg:path d="M 83.638,57.505 C 98.257,57.505 110.759,63.777 115.655,72.576 C 102.935,80.147 88.183,82.013 73.429,78.165 C 66.228,76.163 59.247,73.276 52.12,71.719 C 57.332,63.374 69.498,57.505 83.638,57.505 z " style="font-size:12px;fill:url(#XMLID_4_);stroke:none" id="path595"/>
+ <svg:g transform="matrix(1.38561, 0, 0, 1.38561, -32.2514, -30.5491)" id="g4248">
+ <svg:path d="M 103.21356 24.205935 A 24.311146 23.627199 0 1 1 54.591267,24.205935 A 24.311146 23.627199 0 1 1 103.21356 24.205935 z" transform="matrix(0.148134, 0, 0, 0.152972, 71.9504, 64.0705)" style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:6.64218044;stroke-linecap:square;marker-start:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="path3881"/>
+ <svg:path d="M 87.539971,77.627004 C 87.539971,78.31674 87.591107,91.916423 87.60756,94.355578 C 87.61771,95.860439 89.879004,95.050778 90.026509,95.980703 C 90.2785,97.569343 86.888685,97.025111 86.718511,97.01762 C 85.743882,96.974724 82.425764,97.036144 81.376943,97.036144 C 81.101002,97.036144 77.578516,97.69007 77.314172,96.309196 C 77.071189,95.039902 79.49446,95.29146 79.833236,94.380195 C 81.070282,91.052684 81.154686,84.029315 80.322646,79.891188 C 79.902772,77.802954 76.928763,78.363984 77.263297,76.859643 C 77.479369,75.888015 78.579837,75.778912 79.35102,75.513942 C 81.049574,74.930337 83.123826,75.068206 84.579101,74.012707 C 86.187481,72.846162 87.539971,75.631913 87.539971,77.627004 z " style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.99986994;stroke-linecap:square;marker-start:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" id="path4774"/>
+ </svg:g>
+</svg:svg> \ No newline at end of file
diff --git a/tests/phpunit/data/media/README b/tests/phpunit/data/media/README
new file mode 100644
index 00000000..fe3bc682
--- /dev/null
+++ b/tests/phpunit/data/media/README
@@ -0,0 +1,38 @@
+This directory contains media files for use with the
+tests in includes/media directory.
+
+Image credits:
+
+QA_icon.svg:
+http://es.wikipedia.org/wiki/Archivo:QA_icon.svg
+GNU Lesser General Public License
+~~helix84 (16.4.2007), Philverney (6.12.2005) David Vignoni
+
+Gtk-media-play-ltr.svg
+http://commons.wikimedia.org/wiki/File:Gtk-media-play-ltr.svg
+GNU Lesser General Public License
+http://ftp.gnome.org/pub/GNOME/sources/gnome-themes-extras/0.9/gnome-themes-extras-0.9.0.tar.gz
+David Vignoni
+
+US_states_by_total_state_tax_revenue.svg
+http://commons.wikimedia.org/wiki/File:US_states_by_total_state_tax_revenue.svg
+CC-BY 3.0
+TastyCakes on English Wikipedia
+
+greyscale-na-png.png, rgb-png.png, Xmp-exif-multilingual_test.jpg
+greyscale-png.png, 1bit-png.png, Png-native-test.png, rgb-na-png.png,
+test.tiff, test.jpg, jpeg-comment-multiple.jpg, jpeg-comment-utf.jpg,
+jpeg-comment-iso8859-1.jpg, jpeg-comment-binary.jpg, jpeg-xmp-psir.jpg,
+jpeg-xmp-alt.jpg, animated.gif, exif-user-comment.jpg, animated-xmp.gif,
+iptc-timetest-invalid.jpg, jpeg-iptc-bad-hash.jpg, iptc-timetest.jpg,
+xmp.png, nonanimated.gif, exif-gps.jpg, jpeg-xmp-psir.xmp, jpeg-iptc-good-hash.jpg,
+jpeg-padding-even.jpg, jpeg-padding-odd.jpg
+Are all by Bawolff. I don't think they contain enough originality to
+claim copyright, but on the off chance they do, feel free to use them
+however you feel fit, without restriction.
+
+Animated_PNG_example_bouncing_beach_ball.png
+http://commons.wikimedia.org/wiki/File:Animated_PNG_example_bouncing_beach_ball.png (originally http://www.treebuilder.de/default.asp?file=89031.xml )
+Public Domain
+Holger Will
+
diff --git a/tests/phpunit/data/media/US_states_by_total_state_tax_revenue.svg b/tests/phpunit/data/media/US_states_by_total_state_tax_revenue.svg
new file mode 100644
index 00000000..9afea859
--- /dev/null
+++ b/tests/phpunit/data/media/US_states_by_total_state_tax_revenue.svg
@@ -0,0 +1,248 @@
+<ns0:svg height="592.78998" id="svg2275" version="1.0" width="958.69" ns1:docbase="C:\Users\Adam\Desktop" ns1:docname="Blank_US_Map_with_borders.svg" ns1:version="0.32" ns2:output_extension="org.inkscape.output.svg.inkscape" ns2:version="0.46" xmlns:ns0="http://www.w3.org/2000/svg" xmlns:ns1="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:ns2="http://www.inkscape.org/namespaces/inkscape">
+ <ns0:metadata id="metadata2625">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <ns4:Work rdf:about="" xmlns:ns4="http://creativecommons.org/ns#">
+ <ns5:format xmlns:ns5="http://purl.org/dc/elements/1.1/">image/svg+xml</ns5:format>
+ <ns5:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" xmlns:ns5="http://purl.org/dc/elements/1.1/" />
+ </ns4:Work>
+ </rdf:RDF>
+ </ns0:metadata>
+ <ns0:defs id="defs2623">
+ <ns2:perspective id="perspective226" ns1:type="inkscape:persp3d" ns2:persp3d-origin="479.345 : 197.59666 : 1" ns2:vp_x="0 : 296.39499 : 1" ns2:vp_y="0 : 1000 : 0" ns2:vp_z="958.69 : 296.39499 : 1" />
+ </ns0:defs>
+ <ns1:namedview bordercolor="#666666" borderopacity="1.0" gridtolerance="10.0" guidetolerance="10.0" id="base" objecttolerance="10.0" pagecolor="#ffffff" showgrid="false" ns2:current-layer="svg2275" ns2:cx="479.345" ns2:cy="299.1307" ns2:pageopacity="0.0" ns2:pageshadow="2" ns2:window-height="820" ns2:window-width="1440" ns2:window-x="-8" ns2:window-y="-8" ns2:zoom="0.99941554" />
+ <ns0:path d="M 798.49579,591.98217 L 799.73403,593.07468 L 802.54072,590.88965 L 807.98899,586.51962 L 811.78627,582.48573 L 814.3453,575.59451 L 815.3359,573.82969 L 815.501,570.30004 L 814.75805,570.80427 L 813.76746,573.74564 L 812.28156,578.53588 L 808.97958,583.99844 L 804.52191,588.36847 L 801.05483,590.38542 L 798.49579,591.98217 z M 784.71002,597.19259 L 787.18651,596.52028 L 788.5073,596.26817 L 789.99319,593.83102 L 792.38713,592.15024 L 793.70792,592.65448 L 795.44146,592.99063 L 795.8542,594.08315 L 792.30458,595.34374 L 788.012,596.85644 L 785.61807,598.11703 L 784.71002,597.19259 z M 657.3254,482.07418 L 660.96585,481.47149 L 667.07449,479.28647 L 673.18314,478.78224 L 677.6408,478.10993 L 685.40042,479.95879 L 693.65535,483.99266 L 695.30633,485.50536 L 698.2781,486.6819 L 699.92909,488.69884 L 700.25929,491.55616 L 703.56126,490.21154 L 707.52362,490.21154 L 711.15578,488.1946 L 714.95305,484.49689 L 718.08992,484.66497 L 718.58522,483.48842 L 717.75972,482.47995 L 717.92482,480.46302 L 722.05228,479.62263 L 724.69386,479.62263 L 727.66563,481.13533 L 731.95819,482.64803 L 734.43467,486.51382 L 737.24134,487.52229 L 738.39703,491.05193 L 741.8641,492.73271 L 743.51508,495.42195 L 745.49627,496.09427 L 750.77942,497.43889 L 752.1002,500.63237 L 755.23708,504.49816 L 755.23708,514.41476 L 753.75119,519.28902 L 754.08139,522.14634 L 755.40217,527.18868 L 757.21826,531.39063 L 758.04375,530.8864 L 759.52964,526.18021 L 756.88806,525.17175 L 756.55786,524.49943 L 758.20885,523.82712 L 762.83161,524.83559 L 762.9967,526.51637 L 759.69473,532.23102 L 757.54845,534.75219 L 761.18062,538.61798 L 763.8222,541.81146 L 766.79397,547.35803 L 769.76574,551.3919 L 771.91202,556.60232 L 773.7281,556.93847 L 775.37909,554.75346 L 777.19517,555.93001 L 779.83675,560.13195 L 780.49714,563.82967 L 783.63401,568.36777 L 784.4595,567.02315 L 788.42187,567.3593 L 792.05403,569.7124 L 795.5211,575.09089 L 796.34659,578.62053 L 796.67679,581.64593 L 797.83248,582.6544 L 799.15327,583.15863 L 801.62975,582.15016 L 803.11563,580.46938 L 807.078,580.3013 L 810.21487,578.7886 L 813.02154,575.42704 L 812.52624,573.41011 L 812.19605,570.88894 L 812.85644,568.87201 L 812.52624,566.85507 L 815.00272,565.51045 L 815.33292,561.98081 L 814.67252,560.13195 L 814.17723,547.69419 L 812.85644,539.79453 L 808.23368,531.22255 L 804.60152,525.17175 L 801.95994,519.62517 L 798.98817,516.59977 L 796.0164,508.86819 L 796.84189,507.52356 L 797.99758,506.17894 L 796.34659,503.15354 L 792.21913,499.28775 L 787.26618,493.5731 L 783.46891,487.01806 L 778.02066,477.26954 L 774.21165,467.14054 L 771.56179,458.12552" id="FL_Gulf" style="fill:#cccccc;fill-opacity:0;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 772.07835,458.61245 L 774.21165,467.14054 L 778.02066,477.26954 L 783.46891,487.01806 L 787.26618,493.5731 L 792.21913,499.28775 L 796.34659,503.15354 L 797.99758,506.17894 L 796.84189,507.52356 L 796.0164,508.86819 L 798.98817,516.59977 L 801.95994,519.62517 L 804.60152,525.17175 L 808.23368,531.22255 L 812.85644,539.79453 L 814.17723,547.69419 L 814.67252,560.13195 L 815.33292,561.98081 L 815.00272,565.51045 L 812.52624,566.85507 L 812.85644,568.87201 L 812.19605,570.88894 L 812.52624,573.41011 L 813.02154,575.42704 L 810.21487,578.7886 L 807.078,580.3013 L 803.11563,580.46938 L 801.62975,582.15016 L 799.15327,583.15863 L 797.83248,582.6544 L 796.67679,581.64593 L 796.34659,578.62053 L 795.5211,575.09089 L 792.05403,569.7124 L 788.42187,567.3593 L 784.4595,567.02315 L 783.63401,568.36777 L 780.49714,563.82967 L 779.83675,560.13195 L 777.19517,555.93001 L 775.37909,554.75346 L 773.7281,556.93847 L 771.91202,556.60232 L 769.76574,551.3919 L 766.79397,547.35803 L 763.8222,541.81146 L 761.18062,538.61798 L 757.54845,534.75219 L 759.69473,532.23102 L 762.9967,526.51637 L 762.83161,524.83559 L 758.20885,523.82712 L 756.55786,524.49943 L 756.88806,525.17175 L 759.52964,526.18021 L 758.04375,530.8864 L 757.21826,531.39063 L 755.40217,527.18868 L 754.08139,522.14634 L 753.75119,519.28902 L 755.23708,514.41476 L 755.23708,504.49816 L 752.1002,500.63237 L 750.77942,497.43889 L 745.49627,496.09427 L 743.51508,495.42195 L 741.8641,492.73271 L 738.39703,491.05193 L 737.24134,487.52229 L 734.43467,486.51382 L 731.95819,482.64803 L 727.66563,481.13533 L 724.69386,479.62263 L 722.05228,479.62263 L 717.92482,480.46302 L 717.75972,482.47995 L 718.58522,483.48842 L 718.08992,484.66497 L 714.95305,484.49689 L 711.15578,488.1946 L 707.52362,490.21154 L 703.56126,490.21154 L 700.25929,491.55616 L 699.92909,488.69884 L 698.2781,486.6819 L 695.30633,485.50536 L 693.65535,483.99266 L 685.40042,479.95879 L 677.6408,478.10993 L 673.18314,478.78224 L 667.07449,479.28647 L 660.96585,481.47149 L 657.41261,482.10878 L 657.16963,473.73948 L 654.52806,471.72255 L 652.71197,469.87369 L 653.04217,466.6802 L 663.44338,465.33558 L 689.52898,462.31017 L 696.46313,461.63786 L 702.73688,461.46977 L 705.37846,465.50365 L 706.86434,467.01635 L 714.95419,467.18443 L 726.00404,466.51212 L 747.97393,465.1675 L 753.53546,464.46636 L 758.21005,464.49519 L 758.37515,467.52059 L 761.01673,468.36098 L 761.34692,463.82287 L 759.69594,459.11668 L 760.85163,457.4359 L 766.79518,458.27629 L 772.07835,458.61245 z M 784.71002,597.19259 L 787.18651,596.52028 L 788.5073,596.26817 L 789.99319,593.83102 L 792.38713,592.15024 L 793.70792,592.65448 L 795.44146,592.99063 L 795.8542,594.08315 L 792.30458,595.34374 L 788.012,596.85644 L 785.61807,598.11703 L 784.71002,597.19259 z M 798.49579,591.98217 L 799.73403,593.07468 L 802.54072,590.88965 L 807.98899,586.51962 L 811.78627,582.48573 L 814.3453,575.59451 L 815.3359,573.82969 L 815.501,570.30004 L 814.75805,570.80427 L 813.76746,573.74564 L 812.28156,578.53588 L 808.97958,583.99844 L 804.52191,588.36847 L 801.05483,590.38542 L 798.49579,591.98217 z " id="FL" style="fill:#501616" />
+ <ns0:path d="M 777.85557,425.66962 L 776.04071,426.6776 L 773.39913,425.33297 L 772.73874,423.14795 L 771.41795,419.45024 L 769.10656,417.26521 L 766.46499,416.5929 L 764.814,411.55057 L 762.00732,405.33167 L 757.71476,403.31473 L 755.56847,401.29779 L 754.24768,398.60854 L 752.1014,396.5916 L 749.79002,395.24697 L 747.47863,392.22157 L 744.34176,389.86848 L 739.71899,388.01961 L 739.2237,386.50691 L 736.74722,383.48151 L 736.25192,381.9688 L 732.78485,376.5903 L 729.31778,376.75838 L 725.19031,374.2372 L 723.86952,372.89258 L 723.53932,371.04372 L 724.36481,369.02679 L 726.67619,368.01831 L 726.346,365.8333 L 732.61975,363.14405 L 741.86527,358.43786 L 749.29471,357.59747 L 766.13479,357.09323 L 768.44617,359.11017 L 770.09715,362.47174 L 774.55482,361.9675 L 787.43252,360.45479 L 790.4043,361.29519 L 803.282,369.19486 L 813.60504,377.63896 L 808.06859,383.31398 L 805.42701,389.70094 L 804.93171,396.25598 L 803.28073,397.09637 L 802.12504,399.95369 L 799.64856,400.62601 L 797.50228,404.32372 L 794.69561,407.18104 L 792.38423,410.71068 L 790.73325,411.55107 L 787.10108,415.08071 L 784.12931,415.24879 L 785.1199,418.61034 L 780.00185,424.32499 L 777.85557,425.66962 z " id="SC" style="fill:#e9afaf" />
+ <ns0:path d="M 704.71806,368.52255 L 699.7651,369.36294 L 691.17997,370.53949 L 682.42974,371.46391 L 682.42974,373.73297 L 682.59484,375.91799 L 683.25523,379.44763 L 686.72231,387.68346 L 689.19879,397.93623 L 690.68467,404.3232 L 692.33566,409.36554 L 693.82155,416.5929 L 695.96784,423.14795 L 698.60941,426.6776 L 699.10471,430.20724 L 701.0859,431.04763 L 701.251,433.23265 L 699.4349,438.27499 L 698.93961,441.63656 L 698.77451,443.65349 L 700.4255,448.19161 L 700.7557,453.73818 L 699.9302,456.25936 L 700.5906,457.09975 L 702.07649,457.94014 L 702.73688,461.46977 L 705.37846,465.50365 L 706.86434,467.01635 L 714.95419,467.18443 L 726.00404,466.51212 L 747.97393,465.1675 L 753.53546,464.46636 L 758.21005,464.49519 L 758.37515,467.52059 L 761.01673,468.36098 L 761.34692,463.82287 L 759.69594,459.11668 L 760.85163,457.4359 L 766.79518,458.27629 L 771.87844,458.60669 L 771.08653,452.05785 L 773.39791,441.63702 L 774.88379,437.26699 L 774.3885,434.57775 L 778.51596,427.3504 L 777.90454,425.66937 L 776.04071,426.6776 L 773.39913,425.33297 L 772.73874,423.14795 L 771.41795,419.45024 L 769.10656,417.26521 L 766.46499,416.5929 L 764.814,411.55057 L 762.00732,405.33167 L 757.71476,403.31473 L 755.56847,401.29779 L 754.24768,398.60854 L 752.1014,396.5916 L 749.79002,395.24697 L 747.47863,392.22157 L 744.34176,389.86848 L 739.71899,388.01961 L 739.2237,386.50691 L 736.74722,383.48151 L 736.25192,381.9688 L 732.78485,376.5903 L 729.31778,376.75838 L 725.19031,374.2372 L 723.86952,372.89258 L 723.53932,371.04372 L 724.36481,369.02679 L 726.67619,368.01831 L 726.51109,365.64481 L 724.69501,366.16945 L 718.75146,367.17792 L 711.65221,368.01831 L 704.71806,368.52255 z " id="GA" style="fill:#d35f5f" />
+ <ns0:path d="M 639.33795,481.63956 L 637.68799,465.83981 L 634.88131,446.34274 L 635.04641,431.71994 L 635.8719,399.44893 L 635.7068,382.13688 L 635.87539,375.46299 L 643.79664,375.07759 L 672.19362,372.38834 L 682.58068,371.46391 L 682.42974,373.73297 L 682.59484,375.91799 L 683.25523,379.44763 L 686.72231,387.68346 L 689.19879,397.93623 L 690.68467,404.3232 L 692.33566,409.36554 L 693.82155,416.5929 L 695.96784,423.14795 L 698.60941,426.6776 L 699.10471,430.20724 L 701.0859,431.04763 L 701.251,433.23265 L 699.4349,438.27499 L 698.93961,441.63656 L 698.77451,443.65349 L 700.4255,448.19161 L 700.7557,453.73818 L 699.9302,456.25936 L 700.5906,457.09975 L 702.07649,457.94014 L 702.90198,461.63786 L 696.46313,461.63786 L 689.52898,462.31017 L 663.44338,465.33558 L 653.04217,466.6802 L 652.71197,469.87369 L 654.52806,471.72255 L 657.16963,473.73948 L 657.76284,481.98993 L 651.05994,484.66497 L 648.25327,484.32881 L 651.05994,482.31188 L 651.05994,481.30341 L 647.92307,475.08453 L 645.61169,474.41221 L 644.12581,478.95032 L 642.80502,481.80764 L 642.14462,481.63956 L 639.33795,481.63956 z " id="AL" style="fill:#e9afaf" />
+ <ns0:path d="M 850.23842,306.65958 L 851.98478,311.54471 L 855.61694,318.26782 L 858.09342,320.78899 L 858.75382,323.14208 L 856.27734,323.31016 L 857.10283,323.98247 L 856.77263,328.3525 L 854.13106,329.69712 L 853.47066,331.88214 L 852.14988,334.90754 L 848.35261,336.58832 L 845.87614,336.25216 L 844.39025,336.08408 L 842.73926,334.73946 L 843.06946,336.08408 L 843.06946,337.09255 L 845.05064,337.09255 L 845.87614,338.43717 L 843.89495,344.99221 L 848.18751,344.99221 L 848.84791,346.67299 L 851.15929,344.3199 L 852.48007,343.81567 L 850.49889,347.51338 L 847.36202,352.55572 L 846.04123,352.55572 L 844.88554,352.05149 L 842.07887,352.7238 L 836.79572,355.24497 L 830.19178,360.79154 L 826.72471,365.6658 L 824.74353,372.38892 L 824.24824,374.91008 L 819.46038,375.41432 L 813.43993,377.723 L 803.282,369.19486 L 790.4043,361.29519 L 787.43252,360.45479 L 774.55482,361.9675 L 770.09715,362.47174 L 768.44617,359.11017 L 766.13479,357.09323 L 749.29471,357.59747 L 741.86527,358.43786 L 732.61975,363.14405 L 726.346,365.8333 L 724.69501,366.16945 L 718.75146,367.17792 L 711.65221,368.01831 L 704.71806,368.52255 L 705.04826,363.4802 L 706.86434,361.9675 L 709.67103,361.29519 L 710.33142,357.42939 L 714.62399,354.57206 L 718.58636,353.05935 L 722.87893,349.36164 L 727.33659,347.17662 L 727.99698,343.98313 L 731.95935,339.94926 L 732.61975,339.78119 C 732.61975,339.78119 732.61975,340.95773 733.44524,340.95773 C 734.27073,340.95773 735.42643,341.29389 735.42643,341.29389 L 737.73781,337.59616 L 739.88409,336.92385 L 742.19547,337.26001 L 743.84646,333.56229 L 746.81824,330.87303 L 747.31353,328.68802 L 747.31353,324.57011 L 751.9363,325.32646 L 759.22415,323.98183 L 775.38031,321.96489 L 792.88078,319.27565 L 813.92151,315.35219 L 833.49506,311.37597 L 845.21707,308.35056 L 850.23842,306.65958 z M 854.21672,340.95692 L 856.85831,338.3517 L 860.07773,335.66244 L 861.64617,334.99013 L 861.81127,332.88915 L 861.15088,326.50217 L 859.66499,324.06503 L 859.00459,322.13213 L 859.74753,321.88001 L 862.55422,327.59468 L 862.96697,332.21684 L 862.80187,335.74649 L 859.33479,337.34323 L 856.44555,339.86441 L 855.28987,341.125 L 854.21672,340.95692 z " id="NC" style="fill:#c83737" />
+ <ns0:path d="M 712.3126,329.69649 L 659.31592,334.90691 L 643.2212,336.75577 L 638.50172,337.28883 L 634.55111,337.26001 L 634.55111,341.29389 L 625.96598,341.79812 L 618.86673,342.47043 L 607.53473,342.52544 L 607.26436,348.59252 L 605.08072,355.11718 L 604.06449,358.25292 L 602.68706,362.80789 L 602.35687,365.49714 L 598.22939,367.85023 L 599.71528,371.54796 L 598.72469,376.08606 L 597.15628,377.85089 L 605.49374,377.76685 L 630.09345,375.74991 L 635.54175,375.58184 L 643.79664,375.07759 L 672.19362,372.38834 L 682.58068,371.54796 L 691.17997,370.53949 L 699.7651,369.36294 L 704.71806,368.52255 L 705.04826,363.4802 L 706.86434,361.9675 L 709.67103,361.29519 L 710.33142,357.42939 L 714.62399,354.57206 L 718.58636,353.05935 L 722.87893,349.36164 L 727.33659,347.17662 L 727.99698,343.98313 L 731.95935,339.94926 L 732.61975,339.78119 C 732.61975,339.78119 732.61975,340.95773 733.44524,340.95773 C 734.27073,340.95773 735.42643,341.29389 735.42643,341.29389 L 737.73781,337.59616 L 739.88409,336.92385 L 742.19547,337.26001 L 743.84646,333.56229 L 746.81824,330.87303 L 747.31353,328.68802 L 747.49366,324.59981 L 745.16725,324.65414 L 742.69078,326.67109 L 734.60093,326.83916 L 722.3505,328.8153 L 712.3126,329.69649 z " id="TN" style="fill:#de8787" />
+ <ns0:path d="M 893.09433,183.30123 L 892.6011,178.92994 L 891.77561,174.39182 L 890.04208,168.25697 L 895.90308,166.66023 L 897.55407,167.83677 L 901.02115,172.37489 L 903.99187,176.99768 L 901.01902,178.59507 L 899.69824,178.42699 L 898.54255,180.27585 L 896.06607,182.29279 L 893.09433,183.30123 z " id="RI" style="fill:#f4d7d7" />
+ <ns0:path d="M 893.58963,183.30123 L 892.6011,178.92994 L 891.77561,174.39182 L 890.12463,168.17293 L 884.84146,169.34947 L 862.55312,174.30778 L 863.21351,177.75339 L 864.6994,185.31692 L 864.6994,193.72083 L 863.54371,196.07393 L 865.41508,198.26677 L 870.47581,194.73055 L 874.10797,191.36899 L 876.08916,189.18398 L 876.91465,189.85629 L 879.72132,188.34359 L 885.00447,187.16705 L 893.58963,183.30123 z " id="CT" style="fill:#de8787" />
+ <ns0:path d="M 919.55232,177.09192 L 921.77043,176.37882 L 922.23741,174.59609 L 923.28809,174.71493 L 924.33877,177.09192 L 923.0546,177.56732 L 919.08535,177.68617 L 919.55232,177.09192 z M 909.97943,177.92387 L 912.31427,175.19033 L 913.94868,175.19033 L 915.81656,176.73537 L 913.36497,177.80501 L 911.14686,178.87466 L 909.97943,177.92387 z M 874.44023,155.06282 L 892.27091,150.69278 L 894.5823,150.02047 L 896.72858,146.65891 L 900.54482,144.92957 L 903.4955,149.51759 L 901.01902,154.89608 L 900.68883,156.40879 L 902.67001,159.09803 L 903.8257,158.25764 L 905.64178,158.25764 L 907.95316,160.94689 L 911.91552,167.16577 L 915.54769,167.67001 L 917.85907,166.66154 L 919.67515,164.81268 L 918.84966,161.95536 L 916.70338,160.27458 L 915.21749,161.11497 L 914.2269,159.77034 L 914.7222,159.26611 L 916.86848,159.09803 L 918.68456,159.93842 L 920.66574,162.45959 L 921.65633,165.48499 L 921.98653,168.00616 L 917.69397,169.51886 L 913.73161,171.5358 L 909.76924,176.24198 L 907.78806,177.75468 L 907.78806,176.74621 L 910.26454,175.23351 L 910.75983,173.38466 L 909.93434,170.19118 L 906.96257,171.70388 L 906.13708,173.21658 L 906.63237,175.56967 L 903.82678,177.08172 L 901.02115,172.37489 L 897.55407,167.83677 L 895.90308,166.66023 L 890.04208,168.25697 L 884.84146,169.34947 L 862.55312,174.30778 L 861.56253,168.34101 L 862.22292,157.33189 L 867.50608,156.40745 L 874.44023,155.06282" id="MA" style="fill:#c83737" />
+ <ns0:path d="M 943.28423,76.73985 L 945.26541,78.924863 L 947.57679,82.790656 L 947.57679,84.807591 L 945.43051,89.68185 L 943.44933,90.354162 L 939.98226,93.547643 L 935.02931,99.262292 C 935.02931,99.262292 934.36891,99.262292 933.70852,99.262292 C 933.04813,99.262292 932.71793,97.077279 932.71793,97.077279 L 930.90185,97.245357 L 929.91126,98.758058 L 927.43478,100.27076 L 926.44419,101.78346 L 928.09517,103.29616 L 927.59988,103.96847 L 927.10458,106.8258 L 925.1234,106.65772 L 925.1234,104.97694 L 924.7932,103.63232 L 923.30732,103.96847 L 921.49123,100.60692 L 919.34495,101.95154 L 920.66574,103.46424 L 920.99594,104.64079 L 920.17045,105.98541 L 920.50064,109.17889 L 920.66574,110.85967 L 919.01476,113.54892 L 916.04298,114.05315 L 915.71279,117.07855 L 910.26454,120.27203 L 908.94375,120.77627 L 907.29277,119.26356 L 904.15589,122.96128 L 905.14649,126.32284 L 903.6606,127.66746 L 903.4955,132.20556 L 901.88477,140.12915 L 899.37016,138.9273 L 898.87486,135.73381 L 894.91249,134.55727 L 894.5823,131.69993 L 887.15284,107.32858 L 882.28553,91.967581 L 884.77927,91.608771 L 886.32526,92.034941 L 886.32526,89.345695 L 887.15075,83.631045 L 889.79233,78.756786 L 891.27821,74.554837 L 889.29703,72.033669 L 889.29703,65.814786 L 890.12252,64.806318 L 890.94802,61.948993 L 890.78292,60.436292 L 890.61782,55.393954 L 892.4339,50.351617 L 895.40568,41.107331 L 897.55196,36.737305 L 898.87274,36.737305 L 900.19353,36.905383 L 900.19353,38.081928 L 901.51432,40.435019 L 904.32099,41.107331 L 905.14649,40.266941 L 905.14649,39.258474 L 909.27395,36.233071 L 911.09003,34.384214 L 912.57592,34.552292 L 918.68456,37.073461 L 920.66574,38.081928 L 929.91126,69.176344 L 936.0199,69.176344 L 936.84539,71.193279 L 937.01049,76.235617 L 939.98226,78.588708 L 940.80775,78.588708 L 940.97285,78.084474 L 940.47756,76.907928 L 943.28423,76.73985 z M 921.90732,108.08415 L 923.47577,106.48741 L 924.87911,107.57992 L 925.45696,110.1011 L 923.72342,111.02553 L 921.90732,108.08415 z M 928.75894,101.94929 L 930.57502,103.88219 C 930.57502,103.88219 931.89582,103.96623 931.89582,103.63007 C 931.89582,103.29391 932.14346,101.52909 932.14346,101.52909 L 933.05151,100.6887 L 932.22602,98.839833 L 930.16228,99.596189 L 928.75894,101.94929 z " id="ME" style="fill:#f4d7d7" />
+ <ns0:path d="M 900.54588,144.88986 L 900.85393,143.29871 L 901.96733,139.87704 L 899.37016,138.9273 L 898.87486,135.73381 L 894.91249,134.55727 L 894.5823,131.69993 L 887.15284,107.32858 L 882.45357,92.208279 L 881.5374,92.203019 L 880.87701,93.883799 L 880.21662,93.379565 L 879.22603,92.371097 L 877.74014,94.388032 L 876.76354,100.09176 L 877.08182,105.98396 L 879.063,108.84129 L 879.063,113.04325 L 875.26572,117.2452 L 872.62415,118.42176 L 872.62415,119.5983 L 873.77984,121.44716 L 873.77984,130.35531 L 872.95434,139.93577 L 872.78925,144.97812 L 873.77984,146.32275 L 873.61474,151.02894 L 873.11944,152.8778 L 874.60533,154.97877 L 892.27091,150.69278 L 894.5823,150.02047 L 896.72858,146.65891 L 900.54588,144.88986 z " id="NH" style="fill:#f4d7d7" />
+ <ns0:path d="M 862.38802,157.584 L 861.56253,151.70126 L 858.42565,140.27193 L 857.76525,139.93577 L 854.79347,138.59115 L 855.61896,135.56574 L 854.79347,133.38072 L 852.1519,128.67453 L 853.14249,124.64065 L 852.31699,119.26214 L 849.84051,112.53901 L 849.0178,107.42109 L 876.75058,99.933872 L 877.08182,105.98396 L 879.063,108.84129 L 879.063,113.04325 L 875.26572,117.2452 L 872.62415,118.42176 L 872.62415,119.5983 L 873.77984,121.44716 L 873.77984,130.35531 L 872.95434,139.93577 L 872.78925,144.97812 L 873.77984,146.32275 L 873.61474,151.02894 L 873.11944,152.8778 L 874.60533,154.97877 L 867.50608,156.40745 L 862.38802,157.584 z " id="VT" style="fill:#f4d7d7" />
+ <ns0:path d="M 846.20833,194.22506 L 845.05264,193.21659 L 842.41105,193.04851 L 840.09968,191.03158 L 837.62319,185.485 L 834.55471,184.51732 L 832.17493,182.29151 L 813.18856,186.49346 L 769.27227,195.56969 L 760.19184,197.08239 L 759.43798,189.88537 L 762.17121,188.00743 L 763.492,186.83089 L 764.48259,185.15011 L 766.29867,183.97356 L 768.27985,182.12471 L 768.77515,180.44393 L 770.92143,177.5866 L 772.07712,176.57814 L 771.91202,175.56967 L 770.59123,172.37619 L 768.77515,172.20811 L 766.79397,165.82115 L 769.76574,163.97229 L 774.2234,162.45959 L 778.35086,161.11497 L 781.65283,160.61073 L 788.09167,160.44266 L 790.07285,161.78728 L 791.72384,161.95536 L 793.87012,160.61073 L 796.51169,159.43419 L 801.79484,158.92995 L 803.94112,157.0811 L 805.75721,153.71954 L 807.40819,151.7026 L 809.55447,151.7026 L 811.53565,150.52606 L 811.70075,148.17297 L 810.21487,145.98795 L 809.88467,144.47525 L 811.04036,142.29024 L 811.04036,140.77754 L 809.22428,140.77754 L 807.40819,139.93715 L 806.5827,138.7606 L 806.4176,136.07136 L 812.36115,130.35671 L 813.02154,129.51632 L 814.50743,126.49092 L 817.4792,121.78473 L 820.28587,117.91894 L 822.43215,115.39777 L 824.89861,113.49969 L 828.0455,112.20429 L 833.65885,110.85967 L 836.96082,111.02775 L 841.58358,109.51505 L 849.30966,107.36166 L 849.84051,112.53901 L 852.31699,119.26214 L 853.14249,124.64065 L 852.1519,128.67453 L 854.79347,133.38072 L 855.61896,135.56574 L 854.79347,138.59115 L 857.76525,139.93577 L 858.42565,140.27193 L 861.56253,151.70126 L 862.05782,157.07976 L 861.56253,168.34101 L 862.38802,174.05567 L 863.21351,177.75339 L 864.6994,185.31692 L 864.6994,193.72083 L 863.54371,196.07393 L 865.42216,198.14582 L 865.19266,199.77289 L 863.21147,201.62175 L 863.54167,202.96637 L 864.86246,202.63021 L 866.34835,201.28559 L 868.65972,198.59634 L 869.81541,197.92403 L 871.4664,198.59634 L 873.77778,198.76442 L 881.8676,194.73055 L 884.83937,191.87323 L 886.16016,190.36053 L 890.45272,192.0413 L 886.98565,195.73902 L 883.02329,198.76442 L 875.75896,204.31099 L 873.11738,205.31946 L 867.17384,207.3364 L 863.04638,208.51294 L 861.49899,207.95886 L 860.90212,204.47784 L 861.39742,201.62051 L 861.23232,199.4355 L 858.59075,198.25894 L 853.96798,197.25047 L 850.0056,196.07393 L 846.20833,194.22506 z " id="NY" style="fill:#280b0b" />
+ <ns0:path d="M 846.20833,194.22506 L 844.06205,196.74624 L 844.06205,199.93973 L 842.08086,203.13321 L 841.91576,204.814 L 843.23656,206.15862 L 843.07146,208.6798 L 840.76007,209.85635 L 841.58556,212.71367 L 841.75066,213.89023 L 844.55734,214.22639 L 845.54794,216.91563 L 849.18011,219.43681 L 851.65659,221.11759 L 851.65659,221.95798 L 848.35462,225.15147 L 846.70362,227.50456 L 845.21774,230.3619 L 842.90636,231.70652 L 841.66812,232.46288 L 841.42046,233.72347 L 840.79828,236.43369 L 841.91377,238.76697 L 845.21574,241.79237 L 850.1687,244.14546 L 854.29616,244.81777 L 854.46126,246.33047 L 853.63576,247.33894 L 853.96596,250.19627 L 854.79145,250.19627 L 856.93773,247.6751 L 857.76322,242.63276 L 860.5699,238.43081 L 863.70677,231.70769 L 864.86246,225.99305 L 864.20207,224.8165 L 864.03697,215.06798 L 862.38598,211.53834 L 861.23029,212.37873 L 858.42362,212.71489 L 857.92832,212.21066 L 859.08401,211.20219 L 861.23029,209.18525 L 861.29469,208.048 L 860.90212,204.47784 L 861.39742,201.62051 L 861.23232,199.4355 L 858.59075,198.25894 L 853.96798,197.25047 L 850.0056,196.07393 L 846.20833,194.22506 z " id="NJ" style="fill:#a02c2c" />
+ <ns0:path d="M 841.75066,232.37883 L 842.90636,231.70652 L 845.21774,230.3619 L 846.70362,227.50456 L 848.35462,225.15147 L 851.65659,221.95798 L 851.65659,221.11759 L 849.18011,219.43681 L 845.54794,216.91563 L 844.55734,214.22639 L 841.75066,213.89023 L 841.58556,212.71367 L 840.76007,209.85635 L 843.07146,208.6798 L 843.23656,206.15862 L 841.91576,204.814 L 842.08086,203.13321 L 844.06205,199.93973 L 844.06205,196.74624 L 846.45598,194.22507 L 845.05264,193.21659 L 842.41105,193.04851 L 840.09968,191.03158 L 837.62319,185.485 L 834.55471,184.51732 L 832.17493,182.29151 L 813.18856,186.49346 L 769.27227,195.56969 L 760.19184,197.08239 L 759.68563,189.71729 L 754.08139,195.57094 L 752.7606,196.07518 L 748.46894,199.20351 L 751.4416,219.10066 L 753.16482,230.27806 L 756.81257,250.30417 L 761.54207,249.5232 L 773.73965,247.96108 L 812.47286,239.9916 L 827.66544,237.0562 L 836.14231,235.36944 L 837.45809,234.05962 L 839.60438,232.37883 L 841.75066,232.37883 z " id="PA" style="fill:#782121" />
+ <ns0:path d="M 840.59298,235.90964 L 841.42046,233.72347 L 841.66812,232.37883 L 839.60438,232.37883 L 837.45809,234.05962 L 835.9722,235.57232 L 837.45809,239.94236 L 839.76948,245.8251 L 841.91576,255.9098 L 843.56675,262.46486 L 848.68482,262.29678 L 854.95755,261.03674 L 852.64517,253.38975 L 851.65458,253.89398 L 848.02242,251.37281 L 846.20633,246.49855 L 844.22515,242.80084 L 841.91377,241.79237 L 839.76749,238.09466 L 840.59298,235.90964 z " id="DE" style="fill:#f4d7d7" />
+ <ns0:path d="M 854.95655,260.95325 L 848.68482,262.29678 L 843.56675,262.46486 L 841.91576,255.9098 L 839.76948,245.8251 L 837.45809,239.94236 L 836.14231,235.36944 L 827.66544,237.0562 L 812.47286,239.9916 L 774.22495,247.84224 L 775.38031,253.05285 L 776.37091,258.93558 L 776.7011,258.59942 L 778.84739,256.07825 L 781.15877,252.88476 L 783.63525,252.71668 L 785.12115,251.20398 L 786.93723,248.51473 L 788.25802,249.18705 L 791.22979,248.85089 L 793.87137,246.66588 L 795.92094,245.15492 L 797.80542,244.65068 L 799.48473,245.82549 L 802.45651,247.33819 L 804.43769,249.18705 L 805.67593,250.78379 L 809.88595,252.5486 L 809.88595,255.57402 L 815.49931,256.91864 L 817.48049,258.26326 L 818.47108,256.24633 L 820.78247,257.92711 L 819.29657,261.28868 L 818.96637,264.146 L 817.15029,266.83525 L 817.15029,269.02027 L 817.81068,270.86913 L 822.98233,272.27864 L 827.38511,272.21447 L 830.52198,273.22294 L 832.66826,273.5591 L 833.65885,271.37408 L 832.17296,269.18907 L 832.17296,267.34021 L 829.69649,265.1552 L 827.55021,259.44055 L 828.87099,253.89398 L 828.70589,251.70897 L 827.38511,250.36434 C 827.38511,250.36434 828.87099,248.68356 828.87099,248.01125 C 828.87099,247.33894 829.36629,245.82624 829.36629,245.82624 L 831.34747,244.48162 L 833.32865,242.80084 L 833.82395,243.8093 L 832.33806,245.49008 L 831.01727,249.35588 L 831.34747,250.53242 L 833.16355,250.86858 L 833.65885,256.58323 L 831.51257,257.59169 L 831.84277,261.28941 L 832.33806,261.12133 L 833.49375,259.1044 L 835.14473,260.95325 L 833.49375,262.29788 L 833.16355,265.82751 L 835.80513,269.35715 L 839.76749,269.86138 L 841.41848,269.02099 L 844.72045,274.39949 L 846.53653,274.90372 L 846.53653,278.60143 L 844.22515,283.64377 L 843.72986,290.87112 L 845.21574,294.40076 L 846.70163,294.56884 L 848.68281,290.19881 L 849.5083,286.5011 L 849.6734,279.10567 L 852.81027,274.06333 L 854.95655,266.83598 L 854.95655,260.95325 z M 838.20212,271.12031 L 839.3578,273.72552 L 839.5229,275.57439 L 840.67859,277.50729 C 840.67859,277.50729 841.58664,276.58285 841.58664,276.2467 C 841.58664,275.91054 840.8437,273.05321 840.8437,273.05321 L 840.10075,270.61606 L 838.20212,271.12031 z " id="MD" style="fill:#d35f5f" />
+ <ns0:path d="M 822.59725,272.21447 L 827.38511,272.21447 L 830.52198,273.22294 L 832.66826,273.5591 L 833.65885,271.37408 L 832.17296,269.18907 L 832.17296,267.34021 L 829.69649,265.1552 L 827.55021,259.44055 L 828.87099,253.89398 L 828.70589,251.70897 L 827.38511,250.36434 C 827.38511,250.36434 828.87099,248.68356 828.87099,248.01125 C 828.87099,247.33894 829.36629,245.82624 829.36629,245.82624 L 831.34747,244.48162 L 833.32865,242.80084 L 833.82395,243.8093 L 832.33806,245.49008 L 831.01727,249.35588 L 831.34747,250.53242 L 833.16355,250.86858 L 833.65885,256.58323 L 831.51257,257.59169 L 831.84277,261.28941 L 832.33806,261.12133 L 833.49375,259.1044 L 835.14473,260.95325 L 833.49375,262.29788 L 833.16355,265.82751 L 835.80513,269.35715 L 839.76749,269.86138 L 841.41848,269.02099 L 844.72045,274.39949 L 846.53653,274.90372 L 846.53653,278.60143 L 844.22515,283.64377 L 843.72986,290.87112 L 845.21574,294.40076 L 846.70163,294.56884 L 848.68281,290.19881 L 849.5083,286.5011 L 849.6734,279.10567 L 852.81027,274.06333 L 854.95655,266.83598 L 854.95655,260.95325 M 838.20212,271.12031 L 839.3578,273.72552 L 839.5229,275.57439 L 840.67859,277.50729 C 840.67859,277.50729 841.58664,276.58285 841.58664,276.2467 C 841.58664,275.91054 840.8437,273.05321 840.8437,273.05321 L 840.10075,270.61606 L 838.20212,271.12031 z " id="MD_Atlantic" style="fill:#0000ff;fill-opacity:0;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 774.24466,247.91204 L 775.38031,253.05285 L 776.37091,258.93558 L 776.7011,258.59942 L 778.84739,256.07825 L 781.15877,252.88476 L 783.63525,252.71668 L 785.12115,251.20398 L 786.93723,248.51473 L 788.25802,249.18705 L 791.22979,248.85089 L 793.87137,246.66588 L 795.92094,245.15492 L 797.80542,244.65068 L 799.48473,245.82549 L 802.45651,247.33819 L 804.43769,249.18705 L 805.84103,250.53167 L 804.7679,255.7421 L 798.98943,252.5486 L 794.36667,250.69975 L 794.20157,256.24633 L 793.70628,258.43134 L 792.05529,261.28868 L 791.39489,262.96946 L 788.25802,265.49063 L 787.76272,267.84373 L 784.29564,268.17988 L 783.96545,271.37336 L 782.80976,277.08802 L 780.16818,277.08802 L 778.84739,276.24763 L 777.1964,273.3903 L 775.38031,273.55838 L 775.05012,278.09649 L 772.90384,284.9877 L 767.78577,296.24894 L 768.61127,297.59356 L 768.44617,300.45089 L 766.29989,302.46782 L 764.814,302.13167 L 761.51202,304.65285 L 758.87045,303.64438 L 757.05436,308.51864 C 757.05436,308.51864 753.25709,309.35903 752.59669,309.52711 C 751.9363,309.69518 750.12022,308.18248 750.12022,308.18248 L 747.64373,310.53557 L 745.00215,311.2079 L 742.03037,310.36749 L 740.70958,309.02287 L 738.47074,305.8795 L 735.26132,303.81246 L 732.61975,300.95512 L 729.64798,297.08933 L 728.98758,294.73623 L 726.346,293.22353 L 725.5205,291.54275 L 725.27285,286.0802 L 727.50168,285.99616 L 729.48288,285.15577 L 729.64798,282.29845 L 731.29896,280.78574 L 731.46406,275.57532 L 732.45465,271.54144 L 733.77544,270.86913 L 735.09623,272.04567 L 735.59153,273.89453 L 737.40761,272.88606 L 737.90291,271.20529 L 736.74722,269.35643 L 736.74722,266.83525 L 737.73781,265.49063 L 740.04919,261.96099 L 741.36998,260.44829 L 743.51627,260.95252 L 745.82765,259.27173 L 748.96452,255.7421 L 751.27591,251.70821 L 751.6061,245.82549 L 752.1014,240.61506 L 752.1014,235.74079 L 750.94571,232.54731 L 751.9363,231.0346 L 753.24707,229.68997 L 756.81257,250.30417 L 761.54207,249.5232 L 774.24466,247.91204 z " id="WV" style="fill:#f4d7d7" />
+ <ns0:path d="M 738.61165,306.09768 L 735.42643,309.35903 L 731.13386,313.05675 L 726.51109,318.60333 L 724.69501,320.45219 L 724.69501,322.6372 L 720.73264,324.82222 L 714.95419,328.35186 L 712.28688,329.81359 L 659.31592,334.90691 L 643.2212,336.75577 L 638.50172,337.28883 L 634.55111,337.26001 L 634.55111,341.29389 L 625.96598,341.79812 L 618.86673,342.47043 L 608.21432,342.68419 L 609.24067,341.39196 L 611.46709,339.55999 L 613.56845,338.3715 L 613.80194,335.04372 L 614.73588,333.14213 L 613.09488,330.50244 L 613.91377,328.51994 L 616.22516,326.67109 L 618.37144,325.99877 L 621.17811,327.3434 L 624.81029,328.68802 L 625.96598,328.35186 L 626.13108,325.99877 L 624.81029,323.47759 L 625.14049,321.1245 L 627.12167,319.6118 L 629.76325,318.93949 L 631.41424,318.26718 L 630.58875,316.41831 L 629.92835,314.40138 L 631.08404,313.56099 L 632.15718,310.11537 L 635.2115,308.35056 L 641.15506,307.34209 L 644.78724,306.83786 L 646.27312,308.85479 L 648.08921,309.69518 L 649.90529,306.33362 L 652.87707,304.82092 L 654.85825,306.5017 L 655.68375,307.67825 L 657.83004,307.17401 L 657.66493,303.64438 L 660.63671,301.96359 L 661.7924,301.1232 L 662.94809,302.80398 L 667.73595,302.80398 L 668.56145,300.61896 L 668.23125,298.26587 L 671.20302,294.56815 L 675.99089,290.53428 L 676.48618,285.82809 L 679.29287,285.49193 L 683.25523,283.64307 L 686.06192,281.62613 L 685.73171,279.60919 L 684.24582,278.09649 L 684.82367,275.82744 L 689.03369,275.57532 L 691.51017,274.73493 L 694.48195,276.41571 L 696.13293,280.95382 L 702.07649,281.28997 L 703.89257,283.13884 L 706.03885,283.30692 L 708.51534,281.79422 L 711.65221,282.29845 L 712.973,283.81115 L 715.77968,281.12189 L 717.59577,279.77727 L 719.24675,279.77727 L 719.90714,282.63461 L 721.72324,283.64307 L 725.3554,285.82809 L 725.5205,291.54275 L 726.346,293.22353 L 728.98758,294.73623 L 729.64798,297.08933 L 732.61975,300.95512 L 735.26132,303.81246 L 738.61165,306.09768 z " id="KY" style="fill:#e9afaf" />
+ <ns0:path d="M 748.46982,198.97029 L 741.20371,203.30253 L 737.24134,205.65562 L 733.77427,209.52141 L 729.64681,213.55528 L 726.34484,214.39567 L 723.37307,214.8999 L 717.75972,217.58915 L 715.61344,217.75723 L 712.14638,214.56375 L 706.86322,215.23606 L 704.22165,213.72336 L 701.78994,212.3189 L 696.79333,213.05024 L 686.39211,214.73102 L 678.46737,215.99161 L 679.78816,231.20268 L 681.60425,245.48932 L 684.24582,269.86066 L 684.82367,275.82744 L 689.03369,275.57532 L 691.51017,274.73493 L 694.48195,276.41571 L 696.13293,280.95382 L 702.07649,281.28997 L 703.89257,283.13884 L 706.03885,283.30692 L 708.51534,281.79422 L 711.65221,282.29845 L 712.973,283.81115 L 715.77968,281.12189 L 717.59577,279.77727 L 719.24675,279.77727 L 719.90714,282.63461 L 721.72324,283.64307 L 725.27285,286.0802 L 727.50168,285.99616 L 729.48288,285.15577 L 729.64798,282.29845 L 731.29896,280.78574 L 731.46406,275.57532 L 732.45465,271.54144 L 733.77544,270.86913 L 735.09623,272.04567 L 735.59153,273.89453 L 737.40761,272.88606 L 737.90291,271.20529 L 736.74722,269.35643 L 736.74722,266.83525 L 737.73781,265.49063 L 740.04919,261.96099 L 741.36998,260.44829 L 743.51627,260.95252 L 745.82765,259.27173 L 748.96452,255.7421 L 751.27591,251.70821 L 751.6061,245.82549 L 752.1014,240.61506 L 752.1014,235.74079 L 750.94571,232.54731 L 751.9363,231.0346 L 753.33994,230.27806 L 751.4416,219.10066 L 748.46982,198.97029 z " id="OH" style="fill:#c83737" />
+ <ns0:path d="M 594.42414,81.655837 L 596.29202,79.516552 L 598.51013,78.684606 L 603.99703,74.643722 L 606.33188,74.049474 L 606.79885,74.524879 L 601.54544,79.873103 L 598.1599,81.893534 L 596.05854,82.844333 L 594.42414,81.655837 z M 682.43117,115.05941 L 683.09156,117.66462 L 686.39354,117.8327 L 687.71434,116.57211 C 687.71434,116.57211 687.63178,115.05941 687.30159,114.89133 C 686.97139,114.72326 685.6506,112.95843 685.6506,112.95843 L 683.42177,113.21054 L 681.77077,113.37862 L 681.44058,114.55518 L 682.43117,115.05941 z M 713.13697,180.61201 L 709.835,172.04003 L 707.52362,162.62767 L 705.04714,159.26611 L 702.40557,157.41725 L 700.75458,158.5938 L 696.79222,160.44266 L 694.81104,165.65307 L 692.00436,169.51886 L 690.84867,170.19118 L 689.36279,169.51886 C 689.36279,169.51886 686.72121,168.00616 686.88631,167.33385 C 687.05141,166.66154 687.38161,162.12343 687.38161,162.12343 L 690.84867,160.77881 L 691.67417,157.24918 L 692.33456,154.55993 L 694.81104,152.87915 L 694.48084,142.45832 L 692.82985,140.10523 L 691.50907,139.26484 L 690.68357,137.07982 L 691.50907,136.23943 L 693.16005,136.57559 L 693.32515,134.89481 L 690.84867,132.54172 L 689.52789,129.85247 L 686.88631,129.85247 L 682.26355,128.33977 L 676.6502,124.81014 L 673.84353,124.81014 L 673.18314,125.48245 L 672.19255,124.97821 L 669.05568,122.62512 L 666.0839,124.47398 L 663.11213,126.82707 L 663.44233,130.52479 L 664.43292,130.86094 L 666.5792,131.36518 L 667.07449,132.20556 L 664.43292,133.04595 L 661.79134,133.38211 L 660.30546,135.23097 L 659.97526,137.41598 L 660.30546,139.09676 L 660.63565,144.81141 L 657.00349,146.99642 L 656.34309,146.82834 L 656.34309,142.45832 L 657.66388,139.93715 L 658.32427,137.41598 L 657.49878,136.57559 L 655.5176,137.41598 L 654.52701,141.78601 L 651.72034,142.96255 L 649.90425,144.97949 L 649.73915,145.98795 L 650.39955,146.82834 L 649.73915,149.51759 L 647.42778,150.02182 L 647.42778,151.19837 L 648.25327,153.71954 L 647.09758,160.1065 L 645.44659,164.30845 L 646.10699,169.18271 L 646.60228,170.35925 L 645.77679,172.88042 L 645.44659,173.72081 L 645.1164,176.57814 L 648.74856,182.79702 L 651.72034,189.52014 L 653.20622,194.56247 L 652.38073,199.43673 L 651.39014,205.65562 L 648.91366,211.03411 L 648.58347,213.89143 L 645.43483,217.12572 L 644.67586,217.92491 L 649.40999,217.75643 L 671.86343,215.40333 L 678.13717,214.73102 L 678.46737,215.99161 L 686.39211,214.73102 L 696.79333,213.05024 L 702.12014,212.57103 L 700.75458,211.37027 L 700.91968,209.85756 L 703.06596,205.99177 L 705.10906,204.18493 L 704.88204,198.9325 L 706.51299,197.27212 L 707.62681,196.91556 L 707.85382,193.21785 L 709.42225,190.0664 L 710.49539,190.69668 L 710.66049,191.36899 L 711.48598,191.53707 L 713.46716,190.5286 L 713.13697,180.61201 z M 578.8376,112.43927 L 580.72799,111.3639 L 583.53467,110.52351 L 587.16683,108.17042 L 587.16683,107.16195 L 587.82723,106.48964 L 593.93587,105.48118 L 596.41235,103.46424 L 600.87001,101.27923 L 601.03511,99.934604 L 603.01629,96.909201 L 604.83237,96.068811 L 606.15316,94.219954 L 608.46454,91.866863 L 612.9222,89.345695 L 617.71005,88.841461 L 618.86574,90.018006 L 618.53554,91.026474 L 614.73828,92.034941 L 613.25239,95.228422 L 610.94101,96.068811 L 610.44572,98.58998 L 607.96924,101.95154 L 607.63904,104.64079 L 608.46454,105.14502 L 609.45513,103.96847 L 613.08729,100.94307 L 614.40808,102.28769 L 616.71946,102.28769 L 620.02143,103.29616 L 621.50732,104.47271 L 622.9932,107.66619 L 625.79988,110.52351 L 629.76224,110.35543 L 631.24813,109.34697 L 632.89911,110.69159 L 634.5501,111.19582 L 635.87088,110.35543 L 637.02657,110.35543 L 638.67756,109.34697 L 642.80502,105.64925 L 646.27209,104.47271 L 653.04112,104.13655 L 657.66388,102.11962 L 660.30546,100.77499 L 661.79134,100.94307 L 661.79134,106.8258 L 662.28664,107.16195 L 665.25841,108.00234 L 667.23959,107.49811 L 673.51333,105.81733 L 674.66902,104.64079 L 676.15491,105.14502 L 676.15491,112.37237 L 679.45688,115.56585 L 680.77767,116.23816 L 682.09845,117.24663 L 680.77767,117.58279 L 679.95217,117.24663 L 676.15491,116.7424 L 674.00863,117.41471 L 671.69725,117.24663 L 668.39528,118.75933 L 666.5792,118.75933 L 660.63565,117.41471 L 655.3525,117.58279 L 653.37132,120.27203 L 646.27209,120.94434 L 643.79561,121.78473 L 642.63992,124.97821 L 641.31913,126.15476 L 640.82384,125.98668 L 639.33795,124.3059 L 634.71519,126.82707 L 634.0548,126.82707 L 632.89911,125.14629 L 632.07362,125.31437 L 630.09244,129.85247 L 629.10185,134.05442 L 625.27363,142.51293 L 623.6083,141.31962 L 622.20739,139.89342 L 620.57299,129.197 L 616.83724,128.00851 L 615.43633,125.63153 L 602.59467,122.77914 L 600.02634,121.59066 L 591.62089,119.21367 L 583.21544,118.02518 L 578.8376,112.43927 z " id="MI" style="fill:#c83737" />
+ <ns0:path d="M 363.20447,145.98954 L 351.44763,144.98362 L 318.67723,141.55738 L 302.09981,139.41809 L 273.14771,135.13952 L 252.83454,132.04945 L 251.38528,143.66906 L 247.4644,168.89268 L 242.09425,200.50655 L 240.53069,211.44019 L 238.82546,223.80098 L 245.48808,224.7662 L 262.73518,227.14318 L 271.75391,228.36645 L 292.76047,230.93187 L 330.81813,235.21041 L 355.80134,237.34972 L 360.23755,191.23625 L 361.87193,164.85174 L 363.20447,145.98954 z " id="WY" style="fill:#f4d7d7" />
+ <ns0:path d="M 365.51098,123.96764 L 366.33647,111.87386 L 368.64299,85.935913 L 370.0439,70.247815 L 371.33142,55.452236 L 338.69364,52.032396 L 308.81082,48.334682 L 278.92799,44.132734 L 245.9083,38.586162 L 227.08707,35.056526 L 193.6675,27.848581 L 189.09322,50.043547 L 192.59549,57.887585 L 191.19458,62.64155 L 193.06246,67.395514 L 196.33125,68.821708 L 200.067,79.518133 L 203.80276,83.321298 L 204.26973,84.509794 L 207.772,85.698291 L 208.23897,87.837565 L 201.00095,106.14034 L 201.00095,108.75502 L 203.56928,112.08279 L 204.50321,112.08279 L 209.40639,108.99272 L 210.10685,107.80422 L 211.74124,108.51732 L 211.50775,113.98438 L 214.30957,127.05779 L 217.34487,129.67247 L 218.2788,130.38556 L 220.14668,132.76254 L 219.67972,136.32802 L 220.38017,139.89349 L 221.5476,140.84429 L 223.88244,138.4673 L 226.68426,138.4673 L 229.95305,140.13119 L 232.52138,139.1804 L 236.7241,139.1804 L 240.45985,140.84429 L 243.26167,140.36889 L 243.72864,137.27881 L 246.76394,136.56572 L 248.16485,137.99191 L 248.63182,141.31968 L 251.26853,143.90677 L 252.83454,132.04945 L 273.14771,135.13952 L 302.09981,139.41809 L 318.67723,141.55738 L 351.44763,144.98362 L 363.16317,146.23449 L 364.89307,129.69427 L 365.51098,123.96764 z " id="MT" style="fill:#f4d7d7" />
+ <ns0:path d="M 144.08485,180.96023 L 148.93381,161.76187 L 153.37002,143.34026 L 154.77093,138.94284 L 157.33926,132.76269 L 156.0551,130.38571 L 153.48676,130.50455 L 152.66957,129.43491 L 153.13654,128.24642 L 153.48676,125.0375 L 158.03971,119.33273 L 159.90759,118.85734 L 161.07501,117.66885 L 161.65873,114.34107 L 162.59266,113.62798 L 166.5619,107.56668 L 170.53114,103.05041 L 170.76463,99.128389 L 167.26235,96.394856 L 165.91982,91.819167 L 166.32842,81.776422 L 170.06418,64.662142 L 174.61712,43.031605 L 178.46962,29.00742 L 179.24775,25.053259 L 193.6675,27.848581 L 189.09322,50.043547 L 192.59549,57.887585 L 191.19458,62.64155 L 193.06246,67.395514 L 196.33125,68.821708 L 200.067,79.518133 L 203.80276,83.321298 L 204.26973,84.509794 L 207.772,85.698291 L 208.23897,87.837565 L 201.00095,106.14034 L 201.00095,108.75502 L 203.56928,112.08279 L 204.50321,112.08279 L 209.40639,108.99272 L 210.10685,107.80422 L 211.74124,108.51732 L 211.50775,113.98438 L 214.30957,127.05779 L 217.34487,129.67247 L 218.2788,130.38556 L 220.14668,132.76254 L 219.67972,136.32802 L 220.38017,139.89349 L 221.5476,140.84429 L 223.88244,138.4673 L 226.68426,138.4673 L 229.95305,140.13119 L 232.52138,139.1804 L 236.7241,139.1804 L 240.45985,140.84429 L 243.26167,140.36889 L 243.72864,137.27881 L 246.76394,136.56572 L 248.16485,137.99191 L 248.63182,141.31968 L 251.31689,143.45897 L 247.4644,168.89268 L 242.21095,200.38777 L 237.30774,199.55589 L 228.78555,198.12969 L 218.27875,196.22811 L 206.02081,194.08882 L 193.0624,191.65242 L 184.89044,189.57255 L 175.43432,187.67098 L 165.51122,185.65054 L 144.08485,180.96023 z " id="ID" style="fill:#f4d7d7" />
+ <ns0:path d="M 95.99889,2.9536428 L 100.45655,4.4663441 L 110.36246,7.3236687 L 119.11268,9.3406038 L 139.58489,15.223331 L 163.02887,21.106058 L 179.49525,24.969183 L 178.46962,29.00742 L 174.61712,43.031605 L 170.06418,64.662142 L 166.32842,81.776422 L 166.13328,91.861195 L 151.85237,88.313121 L 136.44238,84.628799 L 120.68217,84.747642 L 120.21521,83.321459 L 114.61157,85.460744 L 110.05862,84.866496 L 107.60703,83.202605 L 106.32286,83.915707 L 101.53644,83.678 L 99.785303,82.251817 L 94.415149,80.112532 L 93.597952,80.231386 L 89.161743,78.686338 L 87.177124,80.587926 L 80.873036,80.231386 L 74.802439,75.952816 L 75.50289,75.12087 L 75.736374,67.039124 L 73.401527,62.998262 L 69.198802,62.404014 L 68.498351,59.789335 L 66.094359,59.304248 L 64.13488,57.747045 L 62.318797,58.755513 L 60.007419,55.73011 L 60.337616,52.704708 L 63.14429,52.368552 L 64.795274,48.166604 L 62.153699,46.990058 L 62.318797,43.124266 L 66.776456,42.451954 L 63.969782,39.59463 L 62.483896,32.199201 L 63.14429,29.173799 L 63.14429,20.93798 L 61.328206,17.576422 L 63.639585,7.8279025 L 65.785865,8.3321363 L 68.262342,11.357539 L 71.069016,14.046786 L 74.370985,16.063721 L 78.993743,18.248734 L 82.130616,18.921045 L 85.102388,20.433747 L 88.569459,21.442214 L 90.880838,21.274136 L 90.880838,18.752967 L 92.201625,17.576422 L 94.347905,16.231799 L 94.678102,17.408344 L 95.008299,19.257201 L 92.696921,19.761435 L 92.366724,21.946448 L 94.182807,23.459149 L 95.338496,25.980318 L 95.99889,27.997253 L 97.484776,27.829175 L 97.649875,26.484552 L 96.659284,25.139928 L 96.163989,21.77837 L 96.989481,19.929513 L 96.329087,18.416812 L 96.329087,16.063721 L 98.14517,12.366006 L 96.989481,9.6767597 L 94.513004,4.634422 L 94.843201,3.7940324 L 95.99889,2.9536428 z M 86.341086,9.169955 L 88.404826,9.001877 L 88.900121,10.430545 L 90.468562,8.7497548 L 92.862495,8.7497548 L 93.687987,10.3465 L 92.119546,12.111324 L 92.779951,12.951724 L 92.037002,15.052704 L 90.63366,15.472893 C 90.63366,15.472893 89.725613,15.556938 89.725613,15.220782 C 89.725613,14.884626 91.21151,12.531524 91.21151,12.531524 L 89.477971,11.943246 L 89.147774,13.455958 L 88.404826,14.12827 L 86.836382,11.775168 L 86.341086,9.169955 z " id="WA" style="fill:#d35f5f" />
+ <ns0:path d="M 224.65378,521.59843 L 226.52879,518.16091 L 228.7163,517.84841 L 229.0288,518.62966 L 226.99754,521.59843 L 224.65378,521.59843 z M 234.49758,518.00466 L 240.43511,520.50467 L 242.46637,520.19217 L 244.02887,516.44215 L 243.40387,513.16089 L 239.34135,512.69214 L 235.43508,514.41089 L 234.49758,518.00466 z M 264.18522,527.69221 L 267.77898,533.00473 L 270.12274,532.69223 L 271.2165,532.22348 L 272.62275,533.47348 L 276.21652,533.31723 L 277.15403,531.91098 L 274.34151,530.19222 L 272.4665,526.59845 L 270.43524,523.16094 L 264.81022,525.97345 L 264.18522,527.69221 z M 283.71656,536.286 L 284.96656,534.41099 L 289.49783,535.34849 L 290.12284,534.87974 L 296.06036,535.50474 L 295.74786,536.75475 L 293.24785,538.161 L 289.02908,537.8485 L 283.71656,536.286 z M 288.87283,541.28602 L 290.74784,545.03604 L 293.7166,543.94228 L 294.0291,542.37977 L 292.4666,540.34851 L 288.87283,540.03601 L 288.87283,541.28602 z M 295.59161,540.19226 L 297.77912,537.37975 L 302.31039,539.72351 L 306.52916,540.81727 L 310.74793,543.47353 L 310.74793,545.34854 L 307.31042,547.0673 L 302.62289,548.0048 L 300.27913,546.59854 L 295.59161,540.19226 z M 311.68544,555.19233 L 313.24794,553.94233 L 316.52921,555.50484 L 323.87299,558.94235 L 327.15426,560.97361 L 328.71676,563.31737 L 330.59177,567.53614 L 334.49804,570.03615 L 334.18554,571.28616 L 330.43552,574.41117 L 326.373,575.81743 L 324.96675,575.19243 L 321.99798,576.91118 L 319.65422,580.0362 L 317.46671,582.84871 L 315.74795,582.69246 L 312.31044,580.19245 L 311.99794,575.81743 L 312.62294,573.47367 L 311.06043,568.00489 L 309.02917,566.28613 L 308.87292,563.78612 L 311.06043,562.84862 L 313.09169,559.87986 L 313.56044,558.94235 L 311.99794,557.22359 L 311.68544,555.19233 z " id="HI_Pacific" style="fill:none;fill-opacity:0.26666697;stroke:#80b0f0;stroke-width:1pt" />
+ <ns0:path d="M 224.65378,521.59843 L 226.52879,518.16091 L 228.7163,517.84841 L 229.0288,518.62966 L 226.99754,521.59843 L 224.65378,521.59843 z M 234.49758,518.00466 L 240.43511,520.50467 L 242.46637,520.19217 L 244.02887,516.44215 L 243.40387,513.16089 L 239.34135,512.69214 L 235.43508,514.41089 L 234.49758,518.00466 z M 264.18522,527.69221 L 267.77898,533.00473 L 270.12274,532.69223 L 271.2165,532.22348 L 272.62275,533.47348 L 276.21652,533.31723 L 277.15403,531.91098 L 274.34151,530.19222 L 272.4665,526.59845 L 270.43524,523.16094 L 264.81022,525.97345 L 264.18522,527.69221 z M 283.71656,536.286 L 284.96656,534.41099 L 289.49783,535.34849 L 290.12284,534.87974 L 296.06036,535.50474 L 295.74786,536.75475 L 293.24785,538.161 L 289.02908,537.8485 L 283.71656,536.286 z M 288.87283,541.28602 L 290.74784,545.03604 L 293.7166,543.94228 L 294.0291,542.37977 L 292.4666,540.34851 L 288.87283,540.03601 L 288.87283,541.28602 z M 295.59161,540.19226 L 297.77912,537.37975 L 302.31039,539.72351 L 306.52916,540.81727 L 310.74793,543.47353 L 310.74793,545.34854 L 307.31042,547.0673 L 302.62289,548.0048 L 300.27913,546.59854 L 295.59161,540.19226 z M 311.68544,555.19233 L 313.24794,553.94233 L 316.52921,555.50484 L 323.87299,558.94235 L 327.15426,560.97361 L 328.71676,563.31737 L 330.59177,567.53614 L 334.49804,570.03615 L 334.18554,571.28616 L 330.43552,574.41117 L 326.373,575.81743 L 324.96675,575.19243 L 321.99798,576.91118 L 319.65422,580.0362 L 317.46671,582.84871 L 315.74795,582.69246 L 312.31044,580.19245 L 311.99794,575.81743 L 312.62294,573.47367 L 311.06043,568.00489 L 309.02917,566.28613 L 308.87292,563.78612 L 311.06043,562.84862 L 313.09169,559.87986 L 313.56044,558.94235 L 311.99794,557.22359 L 311.68544,555.19233 z " id="HI" style="fill:#e9afaf" />
+ <ns0:path d="M 365.08234,342.9472 L 388.2557,344.07626 L 420.00963,345.26475 L 418.60872,369.98537 L 418.14175,388.52584 L 418.37523,390.18973 L 422.81144,393.9929 L 424.9128,395.18139 L 425.61327,394.94369 L 426.31372,392.80441 L 427.71463,394.70599 L 429.81599,394.70599 L 429.81599,393.2798 L 432.6178,394.70599 L 432.15084,398.74687 L 436.35356,398.98457 L 438.92189,400.17306 L 443.12462,400.88615 L 445.69295,402.78774 L 448.02779,400.64846 L 451.53007,401.36155 L 454.0984,404.92703 L 455.03233,404.92703 L 455.03233,407.30401 L 457.36718,408.0171 L 459.70203,405.64012 L 461.5699,406.35321 L 464.13823,406.35321 L 465.07218,408.9679 L 469.97536,410.86948 L 471.37627,410.15638 L 473.24415,405.87781 L 474.41156,405.87781 L 475.57899,408.0171 L 479.78172,408.73019 L 483.51747,410.15638 L 486.55277,411.10718 L 488.42065,410.15638 L 489.1211,407.54171 L 493.55731,407.54171 L 495.65868,408.49249 L 498.46049,406.35321 L 499.62792,406.35321 L 500.32837,408.0171 L 504.53109,408.0171 L 506.16549,405.87781 L 508.03337,406.35321 L 510.13473,408.9679 L 513.40351,410.86948 L 516.6723,411.82028 L 519.47412,413.12761 L 521.80896,415.14805 L 524.84426,413.72186 L 527.64608,414.91035 L 528.33194,426.45357 L 528.34653,436.5409 L 529.04699,446.28652 L 529.74745,450.3274 L 532.31578,454.60597 L 533.24971,459.83533 L 537.68592,465.54009 L 537.9194,468.86787 L 538.61987,469.58096 L 537.9194,478.3758 L 534.88411,483.60516 L 536.5185,485.74444 L 535.81804,488.35912 L 535.11759,495.96547 L 533.71668,499.29324 L 534.00598,503.01936 L 527.40119,504.83432 L 517.33018,509.5405 L 516.33959,511.55743 L 513.69802,513.57437 L 511.55174,515.08707 L 510.23095,515.92746 L 504.4525,521.47403 L 501.64583,523.65904 L 496.19758,527.0206 L 490.41913,529.54177 L 483.98029,533.07141 L 482.16421,534.58411 L 476.22066,538.28182 L 472.7536,538.95414 L 468.79123,544.66878 L 464.66377,545.00494 L 463.67318,547.02188 L 465.98456,549.03881 L 464.49867,554.75346 L 463.17788,559.45964 L 462.0222,563.49351 L 461.1967,568.19969 L 462.0222,570.72086 L 463.83828,577.94821 L 464.82887,584.33517 L 466.64495,587.1925 L 465.65436,588.7052 L 462.51749,590.72214 L 456.73904,586.68827 L 451.1257,585.51172 L 449.80491,586.01595 L 446.50294,585.34364 L 442.21038,582.15016 L 436.92723,580.97362 L 429.1676,577.44398 L 427.02132,573.41011 L 425.70053,566.68699 L 422.39856,564.67006 L 421.73817,562.31697 L 422.39856,561.64466 L 422.72876,558.11502 L 421.40797,557.44271 L 420.74758,556.43424 L 422.06837,551.89614 L 420.41738,549.54304 L 417.11541,548.19842 L 413.64834,543.66032 L 410.01618,536.76912 L 405.72362,534.07988 L 405.88872,532.06294 L 400.44047,519.28902 L 399.61497,514.91899 L 397.79889,512.90206 L 397.63379,511.38936 L 391.52515,505.84279 L 388.88357,502.6493 L 388.88357,501.47276 L 386.242,499.28775 L 379.30786,498.1112 L 371.71333,497.43889 L 368.57646,495.0858 L 363.9537,496.93466 L 360.32154,498.44736 L 358.01016,501.80891 L 357.01957,505.67471 L 352.56191,512.06167 L 350.08543,514.58284 L 347.44386,513.57437 L 345.62777,512.39782 L 343.64659,511.72551 L 339.68423,509.37242 L 339.68423,508.70011 L 337.86815,506.68317 L 332.585,504.49816 L 324.99047,496.43042 L 322.67909,491.55616 L 322.67909,483.15227 L 319.37712,476.42915 L 318.88182,473.57182 L 317.23084,472.56336 L 316.07515,470.37834 L 310.9571,468.19333 L 309.63631,466.51255 L 302.37198,458.27673 L 301.05119,454.91517 L 296.26332,452.56207 L 294.77744,448.02394 L 292.13584,444.99855 L 290.15467,444.49434 L 289.49163,439.63101 L 297.66367,440.34413 L 327.31615,443.19648 L 356.96871,444.86037 L 359.30356,420.13975 L 363.27277,362.37936 L 364.90719,342.88781 L 366.3081,342.91754 M 467.38967,586.18345 L 466.81183,578.788 L 464.00514,571.30851 L 463.42729,563.99709 L 464.99573,555.42509 L 468.38027,548.28176 L 471.92989,542.65112 L 475.14933,538.95339 L 475.80972,539.20552 L 470.9393,546.09673 L 466.48163,552.90391 L 464.41788,559.79513 L 464.08769,565.17365 L 464.99573,571.56063 L 467.63732,579.04012 L 468.13261,584.41863 L 468.29771,585.93134 L 467.38967,586.18345 z " id="TX" style="fill:#280b0b" />
+ <ns0:path d="M 140.74058,399.1133 L 144.96457,398.27159 L 146.48222,396.01346 L 147.06593,393.16108 L 143.33019,392.56684 L 142.86321,391.73489 L 143.33019,389.95216 L 143.44692,383.89086 L 145.54828,383.17775 L 148.46685,380.32538 L 149.05056,375.21486 L 150.56821,371.4117 L 152.55282,369.39125 L 156.0551,367.60852 L 157.68948,366.18234 L 157.80623,363.80536 L 156.75555,363.21111 L 155.93835,362.14147 L 154.65419,356.08016 L 151.85237,350.96965 L 152.44317,347.57226 L 149.86775,344.19525 L 135.04148,320.54427 L 115.19528,290.35661 L 91.963567,255.17727 L 79.318752,235.85757 L 80.989783,229.03046 L 88.111072,202.05171 L 96.399776,169.3682 L 82.624178,165.56503 L 68.848582,161.99955 L 56.006928,157.72098 L 48.301935,155.58169 L 36.627704,152.49162 L 29.426941,149.98419 L 27.813217,154.89608 L 27.648119,162.62767 L 22.364968,174.89736 L 19.228097,177.5866 L 18.8979,178.76315 L 17.081817,179.60354 L 15.595931,183.97356 L 14.770438,187.33512 L 17.577112,191.70515 L 19.228097,196.07518 L 20.383786,199.77289 L 20.053589,206.49601 L 18.237506,209.68949 L 17.577112,215.74029 L 16.586521,219.60608 L 18.402605,223.63995 L 21.209279,228.34614 L 23.520657,233.38847 L 24.841445,237.59042 L 24.511248,240.95198 L 24.181051,241.45621 L 24.181051,243.64123 L 29.959497,250.19627 L 29.464202,252.71743 L 28.803808,255.07053 L 28.143414,257.08746 L 28.308513,265.65943 L 30.454793,269.52523 L 32.435974,272.21447 L 35.242648,272.71871 L 36.233239,275.57603 L 35.07755,279.27375 L 32.93127,280.95453 L 31.775581,280.95453 L 30.950088,284.9884 L 31.445384,288.0138 L 34.747353,292.5519 L 36.398338,298.09847 L 37.884224,302.97273 L 39.205012,306.16621 L 42.672079,312.21702 L 44.157966,314.90627 L 44.653261,317.93167 L 46.304246,318.94014 L 46.304246,321.4613 L 45.478753,323.47824 L 43.66267,330.87367 L 43.167375,332.8906 L 45.643852,335.74793 L 49.936412,336.25216 L 54.559169,338.10102 L 58.521532,340.28603 L 61.493305,340.28603 L 64.465077,343.47951 L 67.106653,348.52185 L 68.262342,350.87494 L 72.224705,353.05995 L 77.177659,353.90034 L 78.663546,356.08536 L 79.32394,359.44692 L 77.838053,360.11923 L 78.16825,361.12769 L 81.470222,361.96808 L 84.276896,362.13616 L 87.248668,367.01042 L 91.211035,371.38045 L 92.036527,373.73354 L 94.678102,378.10356 L 95.008299,381.46512 L 95.008299,391.21364 L 95.503595,393.0625 L 105.7397,394.5752 L 125.88171,397.43253 L 140.74058,399.1133 z M 50.26694,346.75563 L 51.587732,348.35237 L 51.422633,349.697 L 48.120652,349.61296 L 47.542806,348.35237 L 46.88241,346.83966 L 50.26694,346.75563 z M 52.248128,346.75563 L 53.48637,346.08332 L 57.118549,348.26833 L 60.255432,349.52892 L 59.347387,350.20124 L 54.724613,349.94912 L 53.073623,348.26833 L 52.248128,346.75563 z M 73.380807,367.34524 L 75.19689,369.78238 L 76.022393,370.79086 L 77.590834,371.37912 L 78.168673,369.86642 L 77.178082,368.01756 L 74.453952,365.91658 L 73.380807,366.08465 L 73.380807,367.34524 z M 71.89491,376.33744 L 73.711004,379.61497 L 74.949248,381.63192 L 73.463351,381.88403 L 72.142563,380.62344 C 72.142563,380.62344 71.399615,379.11074 71.399615,378.69054 C 71.399615,378.27035 71.399615,376.42148 71.399615,376.42148 L 71.89491,376.33744 z " id="CA" style="fill:#280b0b" />
+ <ns0:path d="M 141.11208,399.22238 L 138.4292,401.4664 L 138.099,402.9791 L 138.5943,403.98756 L 157.91082,415.08071 L 170.2932,422.98037 L 185.31716,431.8885 L 202.4874,442.30933 L 215.03489,444.8305 L 242.33612,448.47703 L 244.42909,434.63935 L 248.26156,406.64864 L 255.37454,351.33455 L 259.72234,319.30647 L 233.45531,315.31482 L 205.67064,310.56085 L 171.53056,303.99238 L 168.54652,322.80241 L 168.07955,323.2778 L 166.32842,326.01134 L 163.76009,325.89248 L 162.47592,323.04011 L 159.67411,322.68356 L 158.74017,321.49507 L 157.80623,321.49507 L 156.87229,322.08932 L 154.88767,323.15896 L 154.77093,330.40875 L 154.53744,332.19149 L 153.95374,345.26489 L 152.43609,347.52302 L 151.85237,350.96965 L 154.65419,356.08016 L 155.93835,362.14147 L 156.75555,363.21111 L 157.80623,363.80536 L 157.68948,366.18234 L 156.0551,367.60852 L 152.55282,369.39125 L 150.56821,371.4117 L 149.05056,375.21486 L 148.46685,380.32538 L 145.54828,383.17775 L 143.44692,383.89086 L 143.33019,389.95216 L 142.86321,391.73489 L 143.33019,392.56684 L 147.06593,393.16108 L 146.48222,396.01346 L 144.96457,398.27159 L 141.11208,399.22238 z " id="AZ" style="fill:#de8787" />
+ <ns0:path d="M 144.08485,180.96023 L 165.51122,185.65054 L 175.43432,187.67098 L 184.89044,189.57255 L 193.12078,191.77126 L 191.89498,197.53545 L 188.27597,215.71936 L 184.42347,236.99336 L 182.43886,246.26359 L 180.22075,260.40663 L 176.83522,277.63975 L 173.56644,293.44668 L 171.55619,304.36486 L 168.54652,322.80241 L 168.07955,323.2778 L 166.32842,326.01134 L 163.76009,325.89248 L 162.47592,323.04011 L 159.67411,322.68356 L 158.74017,321.49507 L 157.80623,321.49507 L 156.87229,322.08932 L 154.88767,323.15896 L 154.77093,330.40875 L 154.53744,332.19149 L 153.95374,345.26489 L 152.43963,347.54764 L 149.86775,344.19525 L 135.04148,320.54427 L 115.19528,290.35661 L 91.963567,255.17727 L 79.318752,235.85757 L 80.989783,229.03046 L 88.111072,202.05171 L 96.166292,169.45944 L 130.48854,177.92533 L 144.49761,181.0154" id="NV" style="fill:#e9afaf" />
+ <ns0:path d="M 259.6056,319.59339 L 233.45531,315.31482 L 205.67064,310.56085 L 171.45228,304.13508 L 173.56644,293.44668 L 176.83522,277.63975 L 180.22075,260.40663 L 182.43886,246.26359 L 184.42347,236.99336 L 188.27597,215.71936 L 191.89498,197.53545 L 193.03322,191.74155 L 206.02081,194.08882 L 218.27875,196.22811 L 228.78555,198.12969 L 237.30774,199.55589 L 242.21095,200.38777 L 240.53069,211.44019 L 238.82546,223.80098 L 245.48808,224.7662 L 262.73518,227.14318 L 272.10412,228.36648 L 268.94498,251.37398 L 265.6762,274.66841 L 261.84373,303.76606 L 260.30605,315.31482 L 259.6056,319.59339 z " id="UT" style="fill:#e9afaf" />
+ <ns0:path d="M 384.05299,331.95365 L 388.2557,263.49654 L 389.8901,240.2021 L 355.80134,237.34972 L 330.81813,235.21041 L 292.76047,230.93187 L 271.63008,228.31722 L 268.94498,251.37398 L 265.6762,274.66841 L 261.84373,303.76606 L 260.30605,315.31482 L 259.72234,319.35569 L 294.86179,323.63426 L 332.58567,328.23704 L 366.04543,330.52751 L 372.84567,331.2406 L 384.5199,331.83485" id="CO" style="fill:#e9afaf" />
+ <ns0:path d="M 290.31977,444.66242 L 289.49163,439.63101 L 297.66367,440.34413 L 327.31615,443.19648 L 356.96871,444.86037 L 359.30356,420.13975 L 363.27277,362.37936 L 364.90719,342.88781 L 366.3081,342.91754 L 366.29351,330.73549 L 332.58567,328.23704 L 294.86179,323.63426 L 259.66398,319.35569 L 255.37454,351.33455 L 248.26156,406.64864 L 244.42909,434.63935 L 242.33612,448.47703 L 258.12559,450.54515 L 259.44637,440.12432 L 276.45152,442.81356 L 290.31977,444.66242 z " id="NM" style="fill:#e9afaf" />
+ <ns0:path d="M 144.38087,180.54003 L 148.93381,161.76187 L 153.37002,143.34026 L 154.77093,138.94284 L 157.33926,132.76269 L 156.0551,130.38571 L 153.48676,130.50455 L 152.66957,129.43491 L 153.13654,128.24642 L 153.48676,125.0375 L 158.03971,119.33273 L 159.90759,118.85734 L 161.07501,117.66885 L 161.65873,114.34107 L 162.59266,113.62798 L 166.5619,107.56668 L 170.53114,103.05041 L 170.76463,99.128389 L 167.26235,96.394856 L 166.19165,92.03947 L 151.85237,88.313121 L 136.44238,84.628799 L 120.68217,84.747642 L 120.21521,83.321459 L 114.61157,85.460744 L 110.05862,84.866496 L 107.60703,83.202605 L 106.32286,83.915707 L 101.53644,83.678 L 99.785303,82.251817 L 94.415149,80.112532 L 93.597952,80.231386 L 89.161743,78.686338 L 87.177124,80.587926 L 80.873036,80.231386 L 74.802439,75.952816 L 75.50289,75.12087 L 75.736374,67.039124 L 73.401527,62.998262 L 69.198802,62.404014 L 68.498351,59.789335 L 66.094359,59.304248 L 60.172517,61.44476 L 57.861139,68.167876 L 54.559169,78.588708 L 51.2572,85.311824 L 46.139147,99.934604 L 39.535209,114.05315 L 31.280285,127.16323 L 29.299104,130.18863 L 28.473611,139.09676 L 27.152823,145.31564 L 29.426941,149.98419 L 36.627704,152.49162 L 48.301935,155.58169 L 56.006928,157.72098 L 68.848582,161.99955 L 82.624178,165.56503 L 96.399776,169.60589 M 144.08485,180.96023 L 96.166292,169.45944 L 130.48854,177.92533 L 144.49761,181.0154" id="OR" style="fill:#e9afaf" />
+ <ns0:path d="M 482.58353,129.91009 L 481.88308,121.11525 L 480.0152,113.50891 L 478.14732,99.484714 L 477.68036,89.263683 L 475.81248,85.698205 L 474.17808,80.468846 L 474.17808,69.772421 L 474.87853,65.731548 L 472.88209,60.014242 L 442.87077,59.427825 L 423.88445,58.755513 L 396.8083,57.410889 L 371.33142,55.452236 L 370.0439,70.247815 L 368.64299,85.935913 L 366.33647,111.87386 L 365.67607,124.49947 L 423.04492,128.24621 L 482.58353,129.91009 z " id="ND" style="fill:#f4d7d7" />
+ <ns0:path d="M 484.10703,208.42015 L 483.13305,206.29529 L 481.4161,203.35887 L 483.28398,198.8426 L 484.6849,192.90014 L 481.88308,190.76086 L 481.4161,187.90848 L 482.35005,185.29379 L 484.21793,185.29379 L 484.6849,178.16284 L 484.45141,146.54897 L 483.98444,143.4589 L 479.78172,139.89342 L 478.61429,137.99183 L 478.61429,136.32794 L 480.71565,134.66406 L 482.11657,133.23787 L 482.4668,129.91009 L 423.04492,128.24621 L 365.67608,124.20534 L 364.89307,129.69427 L 363.24575,146.19243 L 361.87193,164.85174 L 360.23755,191.5928 L 376.1145,192.66245 L 396.66115,193.85093 L 414.87297,195.03943 L 439.15538,196.22791 L 450.12916,195.75252 L 452.23052,198.1295 L 457.1337,201.21959 L 458.30112,202.17037 L 462.73733,200.74418 L 466.70658,200.26879 L 469.50839,200.03109 L 471.37627,201.45728 L 476.51293,203.12117 L 479.54822,204.78505 L 480.0152,206.44894 L 480.94914,208.58823 L 482.81702,208.58823 L 484.10703,208.42015 z " id="SD" style="fill:#f4d7d7" />
+ <ns0:path d="M 496.12564,252.80011 L 497.52656,255.41479 L 497.29307,257.79177 L 499.8614,261.83265 L 503.13018,266.11122 L 496.82609,266.11122 L 451.76325,265.63581 L 410.43676,264.20963 L 388.13897,263.37769 L 389.8901,240.2021 L 355.80134,237.34972 L 360.23755,191.5928 L 376.1145,192.66245 L 396.66115,193.85093 L 414.87297,195.03943 L 439.15538,196.22791 L 450.12916,195.75252 L 452.23052,198.1295 L 457.1337,201.21959 L 458.30112,202.17037 L 462.73733,200.74418 L 466.70658,200.26879 L 469.50839,200.03109 L 471.37627,201.45728 L 476.51293,203.12117 L 479.54822,204.78505 L 480.0152,206.44894 L 480.94914,208.58823 L 482.81702,208.58823 L 484.45141,208.46938 L 485.61883,214.05529 L 488.42065,221.89933 L 489.35459,226.891 L 491.68943,230.69417 L 492.38988,236.16123 L 494.02428,240.4398 L 494.25776,247.33306 L 496.23653,253.05224" id="NE" style="fill:#f4d7d7" />
+ <ns0:path d="M 580.12177,205.73586 L 580.18014,207.63744 L 582.51499,208.35053 L 583.44892,209.53902 L 583.91589,211.44061 L 587.88513,215.00608 L 588.58559,217.38307 L 587.88513,220.94855 L 586.01725,224.75171 L 585.3168,227.36639 L 582.98196,229.26798 L 581.11408,229.98108 L 575.74393,231.40726 L 575.04347,233.30885 L 574.34302,235.44814 L 575.04347,236.87433 L 576.91135,238.53822 L 576.67787,242.81678 L 574.80999,244.48067 L 574.10954,246.14456 L 574.10954,248.99694 L 572.24166,249.47233 L 570.60726,250.66083 L 570.37378,252.08702 L 570.60726,254.22631 L 568.85613,256.06846 L 565.4706,252.56242 L 564.30317,250.18543 L 556.3647,250.89852 L 546.32485,251.37392 L 520.40805,252.32472 L 506.63246,252.56242 L 497.05959,252.80011 L 495.9476,252.92617 L 494.25776,247.33306 L 494.02428,240.4398 L 492.38988,236.16123 L 491.68943,230.69417 L 489.35459,226.891 L 488.42065,221.89933 L 485.61883,214.05529 L 484.45141,208.46938 L 483.0505,206.21125 L 481.4161,203.35887 L 483.28398,198.8426 L 484.6849,192.90014 L 481.88308,190.76086 L 481.4161,187.90848 L 482.35005,185.29379 L 484.10119,185.29379 L 495.89216,185.29379 L 546.55834,184.5807 L 565.00364,183.86761 L 569.20636,183.74877 L 569.90681,187.19538 L 572.24166,188.85927 L 572.47514,190.28546 L 570.37378,193.85093 L 570.60726,197.17871 L 573.1756,201.21959 L 575.74393,202.40807 L 578.77923,202.88347 L 580.12177,205.73586 z " id="IA" style="fill:#e9afaf" />
+ <ns0:path d="M 639.20393,481.84625 L 638.01716,483.15227 L 632.73401,483.15227 L 631.24813,482.31188 L 629.10185,481.97572 L 622.16771,483.99266 L 620.35163,483.15227 L 617.71005,487.52229 L 616.58406,488.3312 L 615.43633,485.74444 L 614.2689,481.70357 L 610.76664,478.3758 L 611.93406,470.53175 L 611.23361,469.58096 L 609.36573,469.81866 L 600.96028,470.53175 L 576.2109,471.24485 L 575.74393,469.58096 L 576.44438,461.26152 L 579.94666,454.84366 L 585.3168,445.33573 L 584.38287,443.19645 L 585.55029,443.19645 L 586.25075,439.86868 L 583.91589,437.96709 L 584.14938,436.0655 L 582.04802,431.31154 L 581.75616,425.75534 L 583.15706,422.99209 L 582.74847,418.47583 L 581.34756,415.38574 L 582.74847,413.95956 L 581.34756,411.82028 L 581.81454,409.91869 L 582.74847,403.50083 L 585.78377,400.64846 L 585.08332,398.50917 L 588.81908,393.0421 L 591.62089,392.09132 L 591.62089,389.47664 L 590.92044,388.05044 L 593.72225,382.58339 L 596.52407,381.39489 L 596.63379,377.84737 L 605.49374,377.76685 L 630.09345,375.74991 L 635.69855,375.51222 L 635.7068,382.13688 L 635.8719,399.44893 L 635.04641,431.71994 L 634.88131,446.34274 L 637.68799,465.83981 L 639.20393,481.84625 z " id="MS" style="fill:#e9afaf" />
+ <ns0:path d="M 632.23973,310.19942 L 632.07463,306.16555 L 632.56993,301.45935 L 634.88131,298.43395 L 636.6974,294.40007 L 639.33898,290.03004 L 638.84368,283.97923 L 637.0276,281.12189 L 636.6974,277.76034 L 637.52289,272.04567 L 637.0276,264.81831 L 635.7068,248.17858 L 634.38601,232.21115 L 633.3949,220.02589 L 636.53128,220.95071 L 638.01716,221.95917 L 639.17285,221.62302 L 641.31913,219.60608 L 644.20888,217.92491 L 649.40999,217.75643 L 671.86343,215.40333 L 678.13717,214.73102 L 678.30227,215.90756 L 679.78816,231.20268 L 681.60425,245.48932 L 684.24582,269.86066 L 684.74112,275.7434 L 684.24582,278.09649 L 685.73171,279.60919 L 686.06192,281.62613 L 683.25523,283.64307 L 679.29287,285.49193 L 676.48618,285.82809 L 675.99089,290.53428 L 671.20302,294.56815 L 668.23125,298.26587 L 668.56145,300.61896 L 667.73595,302.80398 L 662.94809,302.80398 L 661.7924,301.1232 L 660.63671,301.96359 L 657.66493,303.64438 L 657.83004,307.17401 L 655.68375,307.67825 L 654.85825,306.5017 L 652.87707,304.82092 L 649.90529,306.33362 L 648.08921,309.69518 L 646.27312,308.85479 L 644.78724,306.83786 L 641.15506,307.34209 L 635.2115,308.35056 L 632.23973,310.19942 z " id="IN" style="fill:#de8787" />
+ <ns0:path d="M 632.07463,310.03134 L 632.07463,306.16555 L 632.56993,301.45935 L 634.88131,298.43395 L 636.6974,294.40007 L 639.33898,290.03004 L 638.84368,283.97923 L 637.0276,281.12189 L 636.6974,277.76034 L 637.52289,272.04567 L 637.0276,264.81831 L 635.7068,248.17858 L 634.38601,232.21115 L 633.56001,220.10992 L 632.23872,219.26993 L 631.41322,216.58068 L 630.09244,212.71489 L 628.44145,210.86603 L 626.95557,208.17679 L 626.71703,202.46993 L 616.60376,203.83427 L 588.81909,205.617 L 579.94666,205.17133 L 580.18014,207.63744 L 582.51499,208.35053 L 583.44892,209.53902 L 583.91589,211.44061 L 587.88513,215.00608 L 588.58559,217.38307 L 587.88513,220.94855 L 586.01725,224.75171 L 585.3168,227.36639 L 582.98196,229.26798 L 581.11408,229.98108 L 575.74393,231.40726 L 575.04347,233.30885 L 574.34302,235.44814 L 575.04347,236.87433 L 576.91135,238.53822 L 576.67787,242.81678 L 574.80999,244.48067 L 574.10954,246.14456 L 574.10954,248.99694 L 572.24166,249.47233 L 570.60726,250.66083 L 570.37378,252.08702 L 570.60726,254.22631 L 568.85613,255.59306 L 567.80545,258.50488 L 568.27242,262.30804 L 570.60726,269.91439 L 578.07878,277.75843 L 583.68241,281.56161 L 583.44892,286.07787 L 584.38287,287.50407 L 590.92044,287.97946 L 593.72225,289.40566 L 593.0218,293.20882 L 590.68696,299.38898 L 589.98649,302.71676 L 592.32134,306.75762 L 598.85891,312.22469 L 603.52862,312.93778 L 605.62997,318.16715 L 607.73133,321.49492 L 606.7974,324.58499 L 608.43179,328.86356 L 610.29967,331.00285 L 613.32836,330.65102 L 613.91377,328.51994 L 616.22516,326.67109 L 618.37144,325.99877 L 621.17811,327.3434 L 624.81029,328.68802 L 625.96598,328.35186 L 626.13108,325.99877 L 624.81029,323.47759 L 625.14049,321.1245 L 627.12167,319.6118 L 629.76325,318.93949 L 631.41424,318.26718 L 630.58875,316.41831 L 629.92835,314.40138 L 631.08404,313.56099 L 632.07463,310.03134 z " id="IL" style="fill:#782121" />
+ <ns0:path d="M 482.35005,129.91009 L 481.88308,121.11525 L 480.0152,113.50891 L 478.14732,99.484714 L 477.68036,89.263683 L 475.81248,85.698205 L 474.17808,80.468846 L 474.17808,69.772421 L 474.87853,65.731548 L 473.01887,60.063466 L 503.79211,60.100136 L 504.1223,51.528162 L 504.7827,51.360084 L 507.09408,51.864318 L 509.07526,52.704708 L 509.90075,58.419357 L 511.38664,64.806318 L 513.03762,66.487097 L 517.99058,66.487097 L 518.32077,67.999799 L 524.75961,68.335954 L 524.75961,70.520967 L 529.71257,70.520967 L 530.04276,69.176344 L 531.19845,67.999799 L 533.50983,67.327487 L 534.83062,68.335954 L 537.80239,68.335954 L 541.76476,71.025201 L 547.21301,73.54637 L 549.68948,74.050604 L 550.18478,73.042136 L 551.67066,72.537902 L 552.16596,75.563305 L 554.80753,76.907928 L 555.30283,76.403695 L 556.62362,76.571773 L 556.62362,78.756786 L 559.26519,79.765253 L 562.40206,79.765253 L 564.05305,78.924863 L 567.35502,75.563305 L 569.99659,75.059071 L 570.82209,76.907928 L 571.31738,78.252552 L 572.30797,78.252552 L 573.29856,77.412162 L 582.37898,77.076006 L 584.19506,80.269487 L 584.85546,80.269487 L 585.58425,79.142165 L 590.11857,78.756786 L 589.49346,81.126733 L 585.47097,83.036786 L 576.02857,87.25913 L 571.15228,89.345695 L 568.01541,92.034941 L 565.53894,95.732656 L 563.22756,99.766526 L 561.41147,100.60692 L 556.78872,105.81733 L 555.46793,105.98541 L 552.00086,109.17889 L 552.70347,109.74365 L 549.82713,112.55812 L 549.59365,115.4105 L 549.59365,124.20534 L 548.42622,125.86923 L 543.05607,129.91009 L 540.72123,136.09025 L 541.18819,136.32794 L 543.75652,138.46723 L 544.45698,141.79501 L 542.5891,145.12278 L 542.5891,149.16365 L 543.05607,156.0569 L 546.09137,159.14699 L 549.59365,159.14699 L 551.46153,162.47476 L 554.96379,162.95015 L 558.93303,168.89261 L 566.17105,173.17118 L 568.27242,176.02356 L 569.20636,183.86761 L 565.00364,183.86761 L 546.55834,184.5807 L 495.89216,185.29379 L 484.10119,185.29379 L 484.6849,178.16284 L 484.45141,146.54897 L 483.98444,143.4589 L 479.78172,139.89342 L 478.61429,137.99183 L 478.61429,136.32794 L 480.71565,134.66406 L 482.11657,133.23787 L 482.35005,129.91009 z " id="MN" style="fill:#d35f5f" />
+ <ns0:path d="M 626.6436,202.64577 L 626.79047,198.26019 L 625.13948,193.55401 L 624.47909,187.16705 L 623.3234,184.64588 L 624.31399,181.4524 L 625.13948,178.42699 L 626.62537,175.73775 L 625.96497,172.20811 L 625.30458,168.5104 L 625.79988,166.66154 L 627.78106,164.14037 L 627.94616,161.28305 L 627.12066,159.93842 L 627.78106,157.24918 L 628.27635,153.88762 L 631.08303,148.00489 L 634.0548,140.94562 L 634.2199,138.59253 L 633.8897,137.58406 L 633.06421,138.08829 L 628.77165,144.64333 L 625.96497,148.84528 L 623.98379,150.69414 L 623.1583,153.04723 L 621.67241,153.88762 L 620.51673,155.90455 L 619.03084,155.5684 L 618.86574,153.71954 L 620.18653,151.19837 L 622.33281,146.32411 L 624.14889,144.64333 L 625.27363,142.26081 L 623.6083,141.31962 L 622.20739,139.89342 L 620.57299,129.197 L 616.83724,128.00851 L 615.43633,125.63153 L 602.59467,122.77914 L 600.02634,121.59066 L 591.62089,119.21367 L 583.21544,118.02518 L 578.9573,112.40581 L 578.41662,113.71699 L 577.26093,113.54892 L 576.60053,112.37237 L 573.79386,111.53198 L 572.63817,111.70006 L 570.82209,112.70853 L 569.8315,112.03621 L 570.49189,110.01928 L 572.47307,106.8258 L 573.62876,105.64925 L 571.64758,104.13655 L 569.5013,104.97694 L 566.52953,106.99388 L 558.935,110.35543 L 555.96322,111.02775 L 552.99145,110.52351 L 551.98885,109.6104 L 549.82713,112.55812 L 549.59365,115.4105 L 549.59365,124.20534 L 548.42622,125.86923 L 543.05607,129.91009 L 540.72123,136.09025 L 541.18819,136.32794 L 543.75652,138.46723 L 544.45698,141.79501 L 542.5891,145.12278 L 542.5891,149.16365 L 543.05607,156.0569 L 546.09137,159.14699 L 549.59365,159.14699 L 551.46153,162.47476 L 554.96379,162.95015 L 558.93303,168.89261 L 566.17105,173.17118 L 568.27242,176.02356 L 569.20636,183.74877 L 569.90681,187.19538 L 572.24166,188.85927 L 572.47514,190.28546 L 570.37378,193.85093 L 570.60726,197.17871 L 573.1756,201.21959 L 575.74393,202.40807 L 578.77923,202.88347 L 580.03422,205.49817 L 589.40281,205.49815 L 616.60376,203.83427 L 626.6436,202.64577 z " id="WI" style="fill:#de8787" />
+ <ns0:path d="M 568.73938,255.89021 L 565.4706,252.56242 L 564.30317,250.18543 L 556.3647,250.89852 L 546.32485,251.37392 L 520.40805,252.32472 L 506.63246,252.56242 L 498.57727,252.68126 L 496.24239,252.80011 L 497.52656,255.41479 L 497.29307,257.79177 L 499.8614,261.83265 L 503.01343,266.11122 L 506.16549,268.96359 L 508.50034,269.20129 L 509.90124,270.15209 L 509.90124,273.24216 L 508.03337,274.90605 L 507.56639,277.28304 L 509.66775,280.84852 L 512.23609,283.93859 L 514.80442,285.84018 L 516.20533,297.96278 L 515.50487,334.68718 L 515.73836,339.55999 L 516.20533,346.69094 L 540.02077,346.21554 L 563.83621,345.50245 L 585.08332,344.55165 L 596.29058,344.07626 L 598.15846,347.16634 L 597.69149,349.54332 L 594.4227,352.3957 L 593.72225,355.48577 L 600.02634,355.96118 L 605.163,355.24808 L 607.26436,348.59252 L 607.11843,342.73921 L 610.06619,341.22388 L 611.46709,339.55999 L 613.56845,338.3715 L 613.80194,335.04372 L 614.73588,333.14213 L 613.33497,330.61659 L 610.29967,331.00285 L 608.43179,328.86356 L 606.7974,324.58499 L 607.73133,321.49492 L 605.62997,318.16715 L 603.52862,312.93778 L 598.85891,312.22469 L 592.32134,306.75762 L 589.98649,302.71676 L 590.68696,299.38898 L 593.0218,293.20882 L 593.72225,289.40566 L 590.92044,287.97946 L 584.38287,287.50407 L 583.44892,286.07787 L 583.68241,281.56161 L 578.07878,277.75843 L 570.60726,269.91439 L 568.27242,262.30804 L 567.80545,258.50488 L 568.73938,255.89021 z " id="MO" style="fill:#de8787" />
+ <ns0:path d="M 604.99844,354.98628 L 600.02634,355.96118 L 593.72225,355.48577 L 594.4227,352.3957 L 597.69149,349.54332 L 598.15846,347.16634 L 596.29058,344.07626 L 585.08332,344.55165 L 563.83621,345.50245 L 540.02077,346.21554 L 516.20533,346.69094 L 517.83972,353.82189 L 517.83971,362.37904 L 519.24063,373.78867 L 519.47412,413.12761 L 521.80896,415.14805 L 524.84426,413.72186 L 527.64608,414.91035 L 528.31735,426.58728 L 550.99455,426.55757 L 570.60726,425.60677 L 581.63941,425.75534 L 583.15706,422.99209 L 582.74847,418.47583 L 581.34756,415.38574 L 582.74847,413.95956 L 581.34756,411.82028 L 581.81454,409.91869 L 582.74847,403.50083 L 585.78377,400.64846 L 585.08332,398.50917 L 588.81908,393.0421 L 591.62089,392.09132 L 591.62089,389.47664 L 590.92044,388.05044 L 593.72225,382.58339 L 596.52407,381.39489 L 596.22104,377.59524 L 598.72469,376.42223 L 599.71528,371.54796 L 598.22939,367.85023 L 602.35687,365.49714 L 602.68706,362.80789 L 604.06449,358.25292 L 604.99844,354.98628 z " id="AR" style="fill:#e9afaf" />
+ <ns0:path d="M 383.76113,331.71594 L 372.84567,331.2406 L 366.27891,330.73549 L 366.54158,330.94347 L 365.98708,342.94722 L 388.2557,344.07626 L 420.00963,345.26475 L 418.60872,369.98537 L 418.14175,388.52584 L 418.37523,390.18973 L 422.81144,393.9929 L 424.9128,395.18139 L 425.61327,394.94369 L 426.31372,392.80441 L 427.71463,394.70599 L 429.81599,394.70599 L 429.81599,393.2798 L 432.6178,394.70599 L 432.15084,398.74687 L 436.35356,398.98457 L 438.92189,400.17306 L 443.12462,400.88615 L 445.69295,402.78774 L 448.02779,400.64846 L 451.53007,401.36155 L 454.0984,404.92703 L 455.03233,404.92703 L 455.03233,407.30401 L 457.36718,408.0171 L 459.70203,405.64012 L 461.5699,406.35321 L 464.13823,406.35321 L 465.07218,408.9679 L 469.97536,410.86948 L 471.37627,410.15638 L 473.24415,405.87781 L 474.41156,405.87781 L 475.57899,408.0171 L 479.78172,408.73019 L 483.51747,410.15638 L 486.55277,411.10718 L 488.42065,410.15638 L 489.1211,407.54171 L 493.55731,407.54171 L 495.65868,408.49249 L 498.46049,406.35321 L 499.62792,406.35321 L 500.32837,408.0171 L 504.53109,408.0171 L 506.16549,405.87781 L 508.03337,406.35321 L 510.13473,408.9679 L 513.40351,410.86948 L 516.6723,411.82028 L 519.47412,413.48417 L 519.24063,373.78867 L 517.83971,362.37904 L 517.83972,353.82189 L 516.20533,346.69094 L 515.73836,339.55999 L 515.50487,334.92488 L 501.9627,335.75681 L 454.56566,335.28143 L 408.56892,333.14212 L 383.76113,331.71594 z " id="OK" style="fill:#e9afaf" />
+ <ns0:path d="M 515.50487,335.04372 L 501.9627,335.75681 L 454.56566,335.28143 L 408.56892,333.14212 L 383.90706,331.83479 L 388.13897,263.37769 L 410.43676,264.20963 L 451.76325,265.63581 L 496.82609,266.11122 L 503.01343,266.11122 L 506.16549,268.96359 L 508.50034,269.20129 L 509.90124,270.15209 L 509.90124,273.24216 L 508.03337,274.90605 L 507.56639,277.28304 L 509.66775,280.84852 L 512.23609,283.93859 L 514.80442,285.84018 L 516.20533,297.96278 L 515.50487,335.04372 z " id="KS" style="fill:#e9afaf" />
+ <ns0:path d="M 616.71945,488.11058 L 615.43633,485.74444 L 614.2689,481.70357 L 610.76664,478.3758 L 611.93406,470.53175 L 611.23361,469.58096 L 609.36573,469.81866 L 600.96028,470.53175 L 576.2109,471.24485 L 575.74393,469.58096 L 576.44438,461.26152 L 579.94666,454.84366 L 585.3168,445.33573 L 584.38287,443.19645 L 585.55029,443.19645 L 586.25075,439.86868 L 583.91589,437.96709 L 584.14938,436.0655 L 582.04802,431.31154 L 581.69779,425.60677 L 570.60726,425.60677 L 550.99455,426.55757 L 528.31735,426.58728 L 528.34653,436.5409 L 529.04699,446.28652 L 529.74745,450.3274 L 532.31578,454.60597 L 533.24971,459.83533 L 537.68592,465.54009 L 537.9194,468.86787 L 538.61987,469.58096 L 537.9194,478.3758 L 534.88411,483.60516 L 536.5185,485.74444 L 535.81804,488.35912 L 535.11759,495.96547 L 533.71668,499.29324 L 533.84174,503.05325 L 538.62788,501.47276 L 546.88281,501.1366 L 557.44911,504.83432 L 564.05305,506.01086 L 567.85031,504.49816 L 571.15228,505.67471 L 574.45425,506.68317 L 575.27974,504.49816 L 571.97778,503.32162 L 569.3362,503.82585 L 566.52953,502.14507 C 566.52953,502.14507 566.69462,500.80045 567.35502,500.63237 C 568.01541,500.46429 570.49189,499.6239 570.49189,499.6239 L 572.30797,501.1366 L 574.12406,500.12814 L 577.42603,500.80045 L 578.91191,503.32162 L 579.24211,505.67471 L 583.86487,506.01086 L 585.68095,507.85972 L 584.85546,509.5405 L 583.53467,510.38089 L 585.18565,512.06167 L 593.77077,515.75938 L 597.40294,514.41476 L 598.39353,511.89359 L 601.03511,511.22128 L 602.85119,509.70858 L 604.17198,510.71704 L 604.99747,513.74245 L 602.68609,514.58284 L 603.34648,515.25515 L 606.81355,513.91053 L 609.12493,510.38089 L 609.95042,509.87666 L 607.80414,509.5405 L 608.62964,507.85972 L 608.46454,506.34702 L 610.61082,505.84279 L 611.76651,504.49816 L 612.4269,505.33855 C 612.4269,505.33855 612.2618,508.53203 613.08729,508.53203 C 613.91279,508.53203 617.37985,509.20434 617.37985,509.20434 L 621.50732,511.22128 L 622.49791,512.73398 L 625.46968,512.73398 L 626.62537,513.74245 L 628.93675,510.54897 L 628.93675,509.03627 L 627.61596,509.03627 L 624.14889,506.17894 L 618.20535,505.33855 L 614.90338,502.98546 L 616.05907,500.12814 L 618.37045,500.46429 L 618.53554,499.79198 L 616.71946,498.78351 L 616.71946,498.27928 L 620.02143,498.27928 L 621.83751,495.0858 L 620.51673,493.06886 L 620.18653,490.21154 L 618.70064,490.37962 L 616.71946,492.56463 L 616.05907,495.25388 L 612.9222,494.58156 L 611.93161,492.73271 L 613.74769,490.71577 L 615.81141,488.86693 L 616.71945,488.11058 z " id="LA" style="fill:#de8787" />
+ <ns0:path d="M 817.62464,258.28441 L 818.55858,256.38283 L 820.84673,258.09426 L 819.7727,260.66139 L 818.09161,259.04505 L 817.62464,258.28441 z " id="path6656" style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <ns0:g id="g8180" style="fill:#cccccc">
+ <ns0:path d="M 738.7284,305.32516 L 740.70958,309.02287 L 742.03037,310.36749 L 745.00215,311.2079 L 747.64373,310.53557 L 750.12022,308.18248 C 750.12022,308.18248 751.9363,309.69518 752.59669,309.52711 C 753.25709,309.35903 757.05436,308.51864 757.05436,308.51864 L 758.87045,303.64438 L 761.51202,304.65285 L 764.814,302.13167 L 766.29989,302.46782 L 768.44617,300.45089 L 768.61127,297.59356 L 767.78577,296.24894 L 772.90384,284.9877 L 775.05012,278.09649 L 775.38031,273.55838 L 777.1964,273.3903 L 778.84739,276.24763 L 780.16818,277.08802 L 782.80976,277.08802 L 783.96545,271.37336 L 784.29564,268.17988 L 787.76272,267.84373 L 788.25802,265.49063 L 791.39489,262.96946 L 792.05529,261.28868 L 793.70628,258.43134 L 794.20157,256.24633 L 794.36667,250.69975 L 798.98943,252.5486 L 804.7679,255.7421 L 805.59338,250.44764 L 809.88595,252.5486 L 809.88595,255.57402 L 815.49931,256.91864 L 817.48049,258.26326 L 818.47108,256.24633 L 820.78247,257.92711 L 819.29657,261.28868 L 818.96637,264.146 L 817.15029,266.83525 L 817.15029,269.02027 L 817.81068,270.86913 L 822.98233,272.27864 L 824.90863,273.89525 L 830.19178,274.23141 L 832.83336,276.5845 L 836.13533,277.25681 L 837.45611,278.60143 L 836.96082,283.30762 L 837.95141,284.31608 L 838.11651,286.66917 L 839.43729,288.85419 L 839.2722,290.70305 L 835.97023,289.5265 L 835.97023,290.53497 L 837.95141,292.21575 L 837.95141,293.39229 L 839.43729,294.56884 L 840.75808,296.24962 L 840.92318,298.60271 L 838.6118,300.11541 L 838.942,300.61964 L 841.58358,300.11541 L 844.88554,299.4431 L 846.04123,299.27502 L 850.20356,306.58097 L 845.21707,308.35056 L 833.49506,311.37597 L 813.92151,315.35219 L 792.88078,319.27565 L 775.38031,321.96489 L 759.22415,323.98183 L 751.9363,325.32646 L 747.32856,324.68385 L 745.16725,324.65414 L 742.69078,326.67109 L 734.60093,326.83916 L 722.3505,328.8153 L 712.13922,329.78475 L 714.95419,328.35186 L 720.73264,324.82222 L 724.69501,322.6372 L 724.69501,320.45219 L 726.51109,318.60333 L 731.13386,313.05675 L 735.42643,309.35903 L 738.7284,305.32516 z " id="VA" style="fill:#d35f5f" />
+ <ns0:path d="M 845.69487,293.77543 L 844.35758,290.76684 L 844.75381,282.95122 L 847.26331,278.51396 L 847.13123,274.21116 L 853.07478,271.9253 L 852.41438,274.21116 L 849.24449,278.3795 L 848.92256,286.54808 L 848.17135,289.94326 L 846.33876,293.85948 L 845.69487,293.77543 z " id="path3106" style="fill:#d35f5f" ns1:nodetypes="cccccccccccc" />
+ </ns0:g>
+ <ns0:path d="M 467.38967,586.18345 L 466.81183,578.788 L 464.00514,571.30851 L 463.42729,563.99709 L 464.99573,555.42509 L 468.38027,548.28176 L 471.92989,542.65112 L 475.14933,538.95339 L 475.80972,539.20552 L 470.9393,546.09673 L 466.48163,552.90391 L 464.41788,559.79513 L 464.08769,565.17365 L 464.99573,571.56063 L 467.63732,579.04012 L 468.13261,584.41863 L 468.29771,585.93134 L 467.38967,586.18345 z M 465.65436,588.7052 L 466.64495,587.1925 L 464.82887,584.33517 L 463.83828,577.94821 L 462.0222,570.72086 L 461.1967,568.19969 L 462.0222,563.49351 L 463.17788,559.45964 L 464.49867,554.75346 L 465.98456,549.03881 L 463.67318,547.02188 L 464.66377,545.00494 L 468.79123,544.66878 L 472.7536,538.95414 L 476.22066,538.28182 L 482.16421,534.58411 L 483.98029,533.07141 L 490.41913,529.54177 L 496.19758,527.0206 L 501.64583,523.65904 L 504.4525,521.47403 L 510.23095,515.92746 L 511.55174,515.08707 L 513.69802,513.57437 L 516.33959,511.55743 L 517.33018,509.5405 L 527.40119,504.83432 L 534.17024,502.98546" id="TX_Gulf" style="fill:none;fill-opacity:0.26666697;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 900.52373,145.31564 L 900.85393,143.29871 L 902.00961,139.60099" id="NH_Atlantic" style="fill:#0000ff;fill-opacity:0;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 919.55232,177.09192 L 921.77043,176.37882 L 922.23741,174.59609 L 923.28809,174.71493 L 924.33877,177.09192 L 923.0546,177.56732 L 919.08535,177.68617 L 919.55232,177.09192 z M 909.97943,177.92387 L 912.31427,175.19033 L 913.94868,175.19033 L 915.81656,176.73537 L 913.36497,177.80501 L 911.14686,178.87466 L 909.97943,177.92387 z M 903.66061,177.08236 L 906.63237,175.56967 L 906.13708,173.21658 L 906.96257,171.70388 L 909.93434,170.19118 L 910.75983,173.38466 L 910.26454,175.23351 L 907.78806,176.74621 L 907.78806,177.75468 L 909.76924,176.24198 L 913.73161,171.5358 L 917.69397,169.51886 L 921.98653,168.00616 L 921.65633,165.48499 L 920.66574,162.45959 L 918.68456,159.93842 L 916.86848,159.09803 L 914.7222,159.26611 L 914.2269,159.77034 L 915.21749,161.11497 L 916.70338,160.27458 L 918.84966,161.95536 L 919.67515,164.81268 L 917.85907,166.66154 L 915.54769,167.67001 L 911.91552,167.16577 L 907.95316,160.94689 L 905.64178,158.25764 L 903.8257,158.25764 L 902.67001,159.09803 L 900.68883,156.40879 L 901.01902,154.89608 L 903.4955,149.51759 L 900.35862,144.81139" id="MA_Atlantic" style="fill:#0000ff;fill-opacity:0;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 771.67854,458.60093 L 771.08653,452.05785 L 773.39791,441.63702 L 774.88379,437.26699 L 774.3885,434.57775 L 778.51596,427.3504 L 777.85557,425.66962" id="GA_Atlantic" style="fill:#6666e6;fill-opacity:0;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 777.85557,425.66962 L 780.00185,424.32499 L 785.1199,418.61034 L 784.12931,415.24879 L 787.10108,415.08071 L 790.73325,411.55107 L 792.38423,410.71068 L 794.69561,407.18104 L 797.50228,404.32372 L 799.64856,400.62601 L 802.12504,399.95369 L 803.28073,397.09637 L 804.93171,396.25598 L 805.42701,389.70094 L 808.06859,383.31398 L 813.51684,377.43125" id="SC_Atlantic" style="fill:#0000ff;fill-opacity:0;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 813.18663,377.59933 L 819.46038,375.41432 L 824.24824,374.91008 L 824.74353,372.38892 L 826.72471,365.6658 L 830.19178,360.79154 L 836.79572,355.24497 L 842.07887,352.7238 L 844.88554,352.05149 L 846.04123,352.55572 L 847.36202,352.55572 L 850.49889,347.51338 L 852.48007,343.81567 L 851.15929,344.3199 L 848.84791,346.67299 L 848.18751,344.99221 L 843.89495,344.99221 L 845.87614,338.43717 L 845.05064,337.09255 L 843.06946,337.09255 L 843.06946,336.08408 L 842.73926,334.73946 L 844.39025,336.08408 L 845.87614,336.25216 L 848.35261,336.58832 L 852.14988,334.90754 L 853.47066,331.88214 L 854.13106,329.69712 L 856.77263,328.3525 L 857.10283,323.98247 L 856.27734,323.31016 L 858.75382,323.14208 L 858.09342,320.78899 L 855.61694,318.26782 L 851.98478,311.54471 L 850.1687,306.50237 M 854.21672,340.95692 L 856.85831,338.3517 L 860.07773,335.66244 L 861.64617,334.99013 L 861.81127,332.88915 L 861.15088,326.50217 L 859.66499,324.06503 L 859.00459,322.13213 L 859.74753,321.88001 L 862.55422,327.59468 L 862.96697,332.21684 L 862.80187,335.74649 L 859.33479,337.34323 L 856.44555,339.86441 L 855.28987,341.125 L 854.21672,340.95692 z " id="NC_Atlantic" style="fill:#0000ff;fill-opacity:0;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 850.1687,306.50237 L 846.04123,299.27502 L 844.88554,299.4431 L 841.58358,300.11541 L 838.942,300.61964 L 838.6118,300.11541 L 840.92318,298.60271 L 840.75808,296.24962 L 839.43729,294.56884 L 837.95141,293.39229 L 837.95141,292.21575 L 835.97023,290.53497 L 835.97023,289.5265 L 839.2722,290.70305 L 839.43729,288.85419 L 838.11651,286.66917 L 837.95141,284.31608 L 836.96082,283.30762 L 837.45611,278.60143 L 836.13533,277.25681 L 832.83336,276.5845 L 830.19178,274.23141 L 824.90863,273.89525 L 822.59725,272.21447" id="VA_Atlantic" style="fill:#0000ff;fill-opacity:0;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 893.09433,183.30123 L 896.06607,182.29279 L 898.54255,180.27585 L 899.69824,178.42699 L 901.01902,178.59507 L 903.99082,176.91428" id="RI_Atlantic" style="fill:#0000ff;fill-opacity:0;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 865.2752,198.17615 L 870.47581,194.73055 L 874.10797,191.36899 L 876.08916,189.18398 L 876.91465,189.85629 L 879.72132,188.34359 L 885.00447,187.16705 L 893.58963,183.30123" id="CT_Atlantic" style="fill:#0000ff;fill-opacity:0;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 840.75808,236.41389 L 841.91377,238.76697 L 845.21574,241.79237 L 850.1687,244.14546 L 854.29616,244.81777 L 854.46126,246.33047 L 853.63576,247.33894 L 853.96596,250.19627 L 854.79145,250.19627 L 856.93773,247.6751 L 857.76322,242.63276 L 860.5699,238.43081 L 863.70677,231.70769 L 864.86246,225.99305 L 864.20207,224.8165 L 864.03697,215.06798 L 862.38598,211.53834 L 861.23029,212.37873 L 858.42362,212.71489 L 857.92832,212.21066 L 859.08401,211.20219 L 861.23029,209.18525 L 861.06519,207.84063" id="NJ_Atlantic" style="fill:#0000ff;fill-opacity:0;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 861.06519,207.84063 L 863.04638,208.51294 L 867.17384,207.3364 L 873.11738,205.31946 L 875.75896,204.31099 L 883.02329,198.76442 L 886.98565,195.73902 L 890.45272,192.0413 L 886.16016,190.36053 L 884.83937,191.87323 L 881.8676,194.73055 L 873.77778,198.76442 L 871.4664,198.59634 L 869.81541,197.92403 L 868.65972,198.59634 L 866.34835,201.28559 L 864.86246,202.63021 L 863.54167,202.96637 L 863.21147,201.62175 L 865.19266,199.77289 L 865.52285,197.75595" id="NY_Atlantic" style="fill:#0000ff;fill-opacity:0;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 854.95655,260.95325 L 852.64517,253.38975 L 851.65458,253.89398 L 848.02242,251.37281 L 846.20633,246.49855 L 844.22515,242.80084 L 841.91377,241.79237 L 839.76749,238.09466 L 840.59298,235.90964" id="DE_Atlantic" style="fill:#cccccc;fill-opacity:0.26666697;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 29.464207,149.85375 L 27.813223,154.89608 L 27.648124,162.62767 L 22.364973,174.89736 L 19.228102,177.5866 L 18.897905,178.76315 L 17.081822,179.60354 L 15.595936,183.97356 L 14.770444,187.33512 L 17.577118,191.70515 L 19.228102,196.07518 L 20.383791,199.77289 L 20.053595,206.49601 L 18.237511,209.68949 L 17.577118,215.74029 L 16.586527,219.60608 L 18.40261,223.63995 L 21.209284,228.34614 L 23.520662,233.38847 L 24.84145,237.59042 L 24.511253,240.95198 L 24.181056,241.45621 L 24.181056,243.64123 L 29.959503,250.19627 L 29.464207,252.71743 L 28.803813,255.07053 L 28.14342,257.08746 L 28.308518,265.65943 L 30.454798,269.52523 L 32.43598,272.21447 L 35.242654,272.71871 L 36.233244,275.57603 L 35.077555,279.27375 L 32.931275,280.95453 L 31.775586,280.95453 L 30.950093,284.9884 L 31.445389,288.0138 L 34.747358,292.5519 L 36.398343,298.09847 L 37.884229,302.97273 L 39.205017,306.16621 L 42.672085,312.21702 L 44.157971,314.90627 L 44.653266,317.93167 L 46.304251,318.94014 L 46.304251,321.4613 L 45.478759,323.47824 L 43.662676,330.87367 L 43.16738,332.8906 L 45.643857,335.74793 L 49.936417,336.25216 L 54.559175,338.10102 L 58.521538,340.28603 L 61.49331,340.28603 L 64.465083,343.47951 L 67.106658,348.52185 L 68.262347,350.87494 L 72.224711,353.05995 L 77.177665,353.90034 L 78.663551,356.08536 L 79.323945,359.44692 L 77.838059,360.11923 L 78.168256,361.12769 L 81.470225,361.96808 L 84.276899,362.13616 L 87.248671,367.01042 L 91.211035,371.38045 L 92.036527,373.73354 L 94.678102,378.10356 L 95.008299,381.46512 L 95.008299,391.21364 L 95.503595,393.0625 M 50.266945,346.75563 L 51.587737,348.35237 L 51.422639,349.697 L 48.120658,349.61296 L 47.542811,348.35237 L 46.882415,346.83966 L 50.266945,346.75563 z M 52.248133,346.75563 L 53.486376,346.08332 L 57.118555,348.26833 L 60.255437,349.52892 L 59.347393,350.20124 L 54.724619,349.94912 L 53.073629,348.26833 L 52.248133,346.75563 z M 73.380812,367.34524 L 75.196895,369.78238 L 76.022398,370.79086 L 77.590839,371.37912 L 78.168678,369.86642 L 77.178087,368.01756 L 74.453957,365.91658 L 73.380812,366.08465 L 73.380812,367.34524 z M 71.894915,376.33744 L 73.711009,379.61497 L 74.949253,381.63192 L 73.463356,381.88403 L 72.142568,380.62344 C 72.142568,380.62344 71.39962,379.11074 71.39962,378.69054 C 71.39962,378.27035 71.39962,376.42148 71.39962,376.42148 L 71.894915,376.33744 z " id="CA_Pacific" style="fill:none;fill-opacity:0.26666697;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 95.99889,2.9536428 L 94.843201,3.7940324 L 94.513004,4.634422 L 96.989481,9.6767597 L 98.14517,12.366006 L 96.329087,16.063721 L 96.329087,18.416812 L 96.989481,19.929513 L 96.163989,21.77837 L 96.659284,25.139928 L 97.649875,26.484552 L 97.484776,27.829175 L 95.99889,27.997253 L 95.338496,25.980318 L 94.182807,23.459149 L 92.366724,21.946448 L 92.696921,19.761435 L 95.008299,19.257201 L 94.678102,17.408344 L 94.347905,16.231799 L 92.201625,17.576422 L 90.880838,18.752967 L 90.880838,21.274136 L 88.569459,21.442214 L 85.102391,20.433747 L 82.130619,18.921045 L 78.993748,18.248734 L 74.370991,16.063721 L 71.069021,14.046786 L 68.262347,11.357539 L 65.78587,8.3321363 L 63.63959,7.8279025 L 61.328212,17.576422 L 63.144295,20.93798 L 63.144295,29.173799 L 62.483901,32.199201 L 63.969787,39.59463 L 66.776461,42.451954 L 62.318803,43.124266 L 62.153704,46.990058 L 64.79528,48.166604 L 63.144295,52.368552 L 60.337621,52.704708 L 60.007424,55.73011 L 62.318803,58.755513 L 64.134886,57.747045 L 66.446264,59.427825 M 86.341089,9.169955 L 88.404826,9.001877 L 88.900121,10.430545 L 90.468562,8.7497548 L 92.862495,8.7497548 L 93.687987,10.3465 L 92.119546,12.111324 L 92.779951,12.951724 L 92.037002,15.052704 L 90.63366,15.472893 C 90.63366,15.472893 89.725613,15.556938 89.725613,15.220782 C 89.725613,14.884626 91.21151,12.531524 91.21151,12.531524 L 89.477971,11.943246 L 89.147774,13.455958 L 88.404826,14.12827 L 86.836385,11.775168 L 86.341089,9.169955 z " id="WA_Pacific" style="fill:none;fill-opacity:0.26666697;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 66.446264,59.427825 L 60.172522,61.44476 L 57.861144,68.167876 L 54.559175,78.588708 L 51.257205,85.311824 L 46.139153,99.934604 L 39.535214,114.05315 L 31.28029,127.16323 L 29.299109,130.18863 L 28.473616,139.09676 L 27.152829,145.31564 L 29.464207,149.85375" id="OR_Pacific" style="fill:none;fill-opacity:0.26666697;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 639.33795,481.63956 L 642.14462,481.63956 L 642.80502,481.80764 L 644.12581,478.95032 L 645.61169,474.41221 L 647.92307,475.08453 L 651.05994,481.30341 L 651.05994,482.31188 L 648.25327,484.32881 L 651.05994,484.66497 L 658.02586,481.83647" id="AL_Gulf" style="fill:none;fill-opacity:0.26666697;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 616.38926,488.36268 L 617.71005,487.52229 L 620.35163,483.15227 L 622.16771,483.99266 L 629.10185,481.97572 L 631.24813,482.31188 L 632.73401,483.15227 L 638.01716,483.15227 L 639.33795,481.63956" id="MS_Gulf" style="fill:none;fill-opacity:0.26666697;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:path d="M 533.67494,503.06949 L 538.62788,501.47276 L 546.88281,501.1366 L 557.44911,504.83432 L 564.05305,506.01086 L 567.85031,504.49816 L 571.15228,505.67471 L 574.45425,506.68317 L 575.27974,504.49816 L 571.97778,503.32162 L 569.3362,503.82585 L 566.52953,502.14507 C 566.52953,502.14507 566.69462,500.80045 567.35502,500.63237 C 568.01541,500.46429 570.49189,499.6239 570.49189,499.6239 L 572.30797,501.1366 L 574.12406,500.12814 L 577.42603,500.80045 L 578.91191,503.32162 L 579.24211,505.67471 L 583.86487,506.01086 L 585.68095,507.85972 L 584.85546,509.5405 L 583.53467,510.38089 L 585.18565,512.06167 L 593.77077,515.75938 L 597.40294,514.41476 L 598.39353,511.89359 L 601.03511,511.22128 L 602.85119,509.70858 L 604.17198,510.71704 L 604.99747,513.74245 L 602.68609,514.58284 L 603.34648,515.25515 L 606.81355,513.91053 L 609.12493,510.38089 L 609.95042,509.87666 L 607.80414,509.5405 L 608.62964,507.85972 L 608.46454,506.34702 L 610.61082,505.84279 L 611.76651,504.49816 L 612.4269,505.33855 C 612.4269,505.33855 612.2618,508.53203 613.08729,508.53203 C 613.91279,508.53203 617.37985,509.20434 617.37985,509.20434 L 621.50732,511.22128 L 622.49791,512.73398 L 625.46968,512.73398 L 626.62537,513.74245 L 628.93675,510.54897 L 628.93675,509.03627 L 627.61596,509.03627 L 624.14889,506.17894 L 618.20535,505.33855 L 614.90338,502.98546 L 616.05907,500.12814 L 618.37045,500.46429 L 618.53554,499.79198 L 616.71946,498.78351 L 616.71946,498.27928 L 620.02143,498.27928 L 621.83751,495.0858 L 620.51673,493.06886 L 620.18653,490.21154 L 618.70064,490.37962 L 616.71946,492.56463 L 616.05907,495.25388 L 612.9222,494.58156 L 611.93161,492.73271 L 613.74769,490.71577 L 616.38926,488.36268 L 617.2973,487.77441" id="LA_Gulf" style="fill:none;fill-opacity:0.26666697;stroke:#80b0f0;stroke-width:1.06612186pt" />
+ <ns0:g id="Great_Lakes_borders" style="fill:none;stroke:#80b0f0" transform="matrix(1.0566302,0,0,1.0756987,-45.325399,-166.80506)">
+ <ns0:path d="M 652.1875,357.8125 L 649.84375,359.21875 L 647.8125,361.09375 L 646.71875,361.40625 L 645.3125,360.46875 L 642.18749,359.53125" id="IN_Great_Lakes" style="fill:none;stroke:#80b0f0;stroke-width:1pt" />
+ <ns0:path d="M 712.53906,338.43751 L 712.8125,334.6875 L 714.29687,331.75782 L 715.50782,330.39062 M 717.85156,323.16407 L 714.6875,315 L 712.5,306.25 L 710.15625,303.125 L 707.65625,301.40625 L 706.09375,302.5 L 702.34375,304.21875 L 700.46875,309.0625 L 697.8125,312.65625 L 696.71875,313.28125 L 695.3125,312.65625 C 695.3125,312.65625 692.8125,311.25 692.96875,310.625 C 693.125,310 693.4375,305.78125 693.4375,305.78125 L 696.71875,304.53125 L 697.5,301.25 L 698.125,298.75 L 700.46875,297.1875 L 700.15625,287.5 L 698.59375,285.3125 L 697.34375,284.53125 L 696.5625,282.5 L 697.34375,281.71875 L 698.90625,282.03125 L 699.0625,280.46875 L 696.71875,278.28125 L 695.46875,275.78125 L 692.96875,275.78125 L 688.59375,274.375 L 683.28125,271.09375 L 680.625,271.09375 L 680,271.71875 L 679.0625,271.25 L 676.09375,269.0625 L 673.28125,270.78125 L 670.46875,272.96875 L 670.78125,276.40625 L 671.71875,276.71875 L 673.75,277.1875 L 674.21875,277.96875 L 671.71875,278.75 L 669.21875,279.0625 L 667.8125,280.78125 L 667.5,282.8125 L 667.8125,284.375 L 668.125,289.6875 L 664.6875,291.71875 L 664.0625,291.5625 L 664.0625,287.5 L 665.3125,285.15625 L 665.9375,282.8125 L 665.15625,282.03125 L 663.28125,282.8125 L 662.34375,286.875 L 659.6875,287.96875 L 657.96875,289.84375 L 657.8125,290.78125 L 658.4375,291.5625 L 657.8125,294.0625 L 655.625,294.53125 L 655.625,295.625 L 656.40625,297.96875 L 655.3125,303.90625 L 653.75,307.8125 L 654.375,312.34375 L 654.84375,313.4375 L 654.0625,315.78125 L 653.75,316.5625 L 653.4375,319.21875 L 656.875,325 L 659.6875,331.25 L 661.09375,335.9375 L 660.3125,340.46875 L 659.375,346.25 L 657.03125,351.25 L 656.71875,353.90625 L 654.84375,356.25 L 652.1875,357.8125 M 605.4621,230.97629 L 607.22987,228.98755 L 609.3291,228.21415 L 614.52193,224.45763 L 616.73164,223.9052 L 617.17359,224.34715 L 612.20173,229.31901 L 608.99764,231.19726 L 607.0089,232.08115 L 605.4621,230.97629 z M 634.68749,287.50003 L 638.28125,279.6875 L 639.21875,275.78125 L 641.09375,271.5625 L 641.875,271.40625 L 642.96875,272.96875 L 643.59375,272.96875 L 647.96875,270.625 L 649.375,272.1875 L 649.84375,272.34375 L 651.09375,271.25 L 652.1875,268.28125 L 654.53125,267.5 L 661.25,266.875 L 663.125,264.375 L 668.125,264.21875 L 673.75,265.46875 L 675.46875,265.46875 L 678.59375,264.0625 L 680.78125,264.21875 L 682.8125,263.59375 L 686.40625,264.0625 L 687.1875,264.375 L 688.4375,264.0625 L 687.1875,263.125 L 685.9375,262.5 L 682.8125,259.53125 L 682.8125,252.8125 L 681.40625,252.34375 L 680.3125,253.4375 L 674.375,255 L 672.5,255.46875 L 669.6875,254.6875 L 669.21875,254.375 L 669.21875,248.90625 L 667.8125,248.75 L 665.3125,250 L 660.9375,251.875 L 654.53125,252.1875 L 651.25,253.28125 L 647.34375,256.71875 L 645.78125,257.65625 L 644.6875,257.65625 L 643.4375,258.4375 L 641.875,257.96875 L 640.3125,256.71875 L 638.90625,257.65625 L 635.15625,257.8125 L 632.5,255.15625 L 631.09375,252.1875 L 629.6875,251.09375 L 626.5625,250.15625 L 624.375,250.15625 L 623.125,248.90625 L 619.6875,251.71875 L 618.75,252.8125 L 617.96875,252.34375 L 618.28125,249.84375 L 620.625,246.71875 L 621.09375,244.375 L 623.28125,243.59375 L 624.6875,240.625 L 628.28125,239.6875 L 628.59375,238.75 L 627.5,237.65625 L 622.96875,238.125 L 618.75,240.46875 L 616.5625,242.65625 L 615.3125,244.375 L 613.59375,245.15625 L 611.71875,247.96875 L 611.5625,249.21875 L 607.34375,251.25 L 605,253.125 L 599.21875,254.0625 L 598.59375,254.6875 L 598.59375,255.625 L 595.15625,257.8125 L 592.5,258.59375 L 590.9375,259.53125 M 688.75238,262.0292 L 689.37738,264.45108 L 692.50239,264.60733 L 693.7524,263.43545 C 693.7524,263.43545 693.67427,262.0292 693.36177,261.87295 C 693.04927,261.7167 691.79927,260.07607 691.79927,260.07607 L 689.68989,260.31044 L 688.12738,260.46669 L 687.81488,261.56045 L 688.75238,262.0292 z M 707.34375,352.8125 L 706.09375,351.5625 L 706.25,350.15625 L 708.28125,346.5625 L 710.42969,344.60937" id="MI_Great_Lakes" style="fill:none;stroke:#80b0f0;stroke-width:1pt" />
+ <ns0:path d="M 762.03126,331.40624 L 756.5625,336.875 L 755.3125,337.34375 L 751.25001,340.46875" id="PA_Great_Lakes" style="fill:none;stroke:#80b0f0;stroke-width:1pt" />
+ <ns0:path d="M 773.4375,318.28125 L 773.59375,319.21875 L 772.5,320.15625 L 770.46875,322.8125 L 770,324.375 L 768.125,326.09375 L 766.40625,327.1875 L 765.46875,328.75 L 764.21875,329.84375 L 761.56251,331.71874 M 823.75,260.46875 L 821.25,262.34375 L 819.21875,264.6875 L 816.5625,268.28125 L 813.75,272.65625 L 812.34375,275.46875 L 811.71875,276.25 L 806.09375,281.5625 L 806.25,284.0625 L 807.03125,285.15625 L 808.75,285.9375 L 810.46875,285.9375 L 810.46875,287.34375 L 809.375,289.375 L 809.6875,290.78125 L 811.09375,292.8125 L 810.9375,295 L 809.0625,296.09375 L 807.03125,296.09375 L 805.46875,297.96875 L 803.75,301.09375 L 801.71875,302.8125 L 796.71875,303.28125 L 794.21875,304.375 L 792.1875,305.625 L 790.625,305.46875 L 788.75,304.21875 L 782.65625,304.375 L 779.53125,304.84375 L 775.625,306.09375 L 771.40625,307.5 L 768.59375,309.21875" id="NY_Great_Lakes" style="fill:none;stroke:#80b0f0;stroke-width:1pt" />
+ <ns0:path d="M 751.40626,340.46875 L 744.375,344.0625 L 740.625,346.25 L 737.34375,349.84375 L 733.4375,353.59375 L 730.3125,354.375 L 727.5,354.84375 L 722.1875,357.34375 L 720.15625,357.5 L 716.875,354.53125 L 711.875,355.15625 L 709.375,353.75 L 706.71874,352.34374" id="OH_Great_Lakes" style="fill:none;stroke:#80b0f0;stroke-width:1pt" />
+ <ns0:path d="M 642.5,359.6875 L 641.25,358.90625 L 640.46875,356.40625 L 639.21875,352.8125 L 637.65625,351.09375 L 636.25,348.59375 L 636.09375,343.12504" id="IL_Great_Lakes" style="fill:none;stroke:#80b0f0;stroke-width:1pt" />
+ <ns0:path d="M 590.9375,259.53125 L 590.3125,260.78125 L 589.21875,260.625 L 588.59375,259.53125 L 585.9375,258.75 L 584.84375,258.90625 L 583.125,259.84375 L 582.1875,259.21875 L 582.8125,257.34375 L 584.6875,254.375 L 585.78125,253.28125 L 583.90625,251.875 L 581.875,252.65625 L 579.0625,254.53125 L 571.875,257.65625 L 569.0625,258.28125 L 566.25,257.8125 L 565.46875,256.875 M 636.25,343.43744 L 636.09375,339.375 L 634.53125,335 L 633.90625,329.0625 L 632.8125,326.71875 L 633.75,323.75 L 634.53125,320.9375 L 635.9375,318.4375 L 635.3125,315.15625 L 634.6875,311.71875 L 635.15625,310 L 637.03125,307.65625 L 637.1875,305 L 636.40625,303.75 L 637.03125,301.25 L 637.5,298.125 L 640.15625,292.65625 L 642.96875,286.09375 L 643.125,283.90625 L 642.8125,282.96875 L 642.03125,283.4375 L 637.96875,289.53125 L 635.3125,293.4375 L 633.4375,295.15625 L 632.65625,297.34375 L 631.25,298.125 L 630.15625,300 L 628.75,299.6875 L 628.59375,297.96875 L 629.84375,295.625 L 631.875,291.09375 L 633.59375,289.53125 L 634.68749,287.03127" id="WI_Great_Lakes" style="fill:none;stroke:#80b0f0;stroke-width:1pt" />
+ <ns0:path d="M 565.9375,257.34374 L 565.3125,256.5625 L 568.59375,253.59375 L 569.84375,253.4375 L 574.21875,248.59375 L 575.9375,247.8125 L 578.125,244.0625 L 580.46875,240.625 L 583.4375,238.125 L 587.5,236.40625 L 595,232.8125 L 597.8125,232.03125 C 597.8125,232.03125 600.78125,230.3125 601.09375,229.6875 C 601.40625,229.0625 601.71875,228.28125 601.71875,228.28125" id="MN_Great_Lakes" style="fill:none;stroke:#80b0f0;stroke-width:1pt" />
+ </ns0:g>
+ <ns0:path d="M 152.15345,458.16063 L 151.84095,540.66102 L 153.40345,541.59852 L 156.37222,541.75477 L 157.77847,540.66102 L 160.27848,540.66102 L 160.43474,543.47353 L 167.15352,550.03606 L 167.62227,552.53607 L 170.90353,550.66106 L 171.52854,550.50481 L 171.84104,547.53605 L 173.24729,545.97354 L 174.34105,545.81729 L 176.21606,544.41103 L 179.18482,546.44229 L 179.80983,549.25481 L 181.68483,550.34856 L 182.77859,552.69232 L 186.52861,554.41108 L 189.80987,560.19236 L 192.46613,563.94237 L 194.65364,566.59864 L 196.0599,570.1924 L 200.90367,571.91116 L 205.9037,573.94242 L 206.8412,578.16119 L 207.30995,581.12995 L 206.37245,584.41122 L 204.65369,586.59873 L 203.09118,585.81748 L 201.68493,582.84871 L 199.02866,581.44246 L 197.30991,580.3487 L 196.52865,581.12995 L 197.93491,583.78622 L 198.09116,587.37998 L 196.9974,587.84873 L 195.1224,585.97373 L 193.09114,584.72372 L 193.55989,586.28623 L 194.80989,588.00499 L 194.02864,588.78624 C 194.02864,588.78624 193.24739,588.47374 192.77864,587.84873 C 192.30988,587.22373 190.74738,584.56747 190.74738,584.56747 L 189.80987,582.37996 C 189.80987,582.37996 189.49737,583.62997 188.87237,583.31746 C 188.24736,583.00496 187.62236,581.91121 187.62236,581.91121 L 189.34112,580.0362 L 187.93486,578.62994 L 187.93486,573.78617 L 187.15361,573.78617 L 186.37236,577.06743 L 185.2786,577.53619 L 184.3411,573.94242 L 183.71609,570.34865 L 182.93484,569.8799 L 183.24734,575.34868 L 183.24734,576.44243 L 181.84108,575.19243 L 178.40357,569.41115 L 176.37231,568.9424 L 175.74731,565.34863 L 174.1848,562.53612 L 172.62229,561.44236 L 172.62229,559.25485 L 174.65355,558.00485 L 174.1848,557.69235 L 171.68479,558.31735 L 168.40352,555.97359 L 165.90351,553.16107 L 161.21599,550.66106 L 157.30972,548.16105 L 158.55973,545.03604 L 158.55973,543.47353 L 156.84097,545.03604 L 154.02846,546.12979 L 150.43469,545.03604 L 144.96591,542.69228 L 139.65339,542.69228 L 139.02839,543.16103 L 132.77836,539.41101 L 130.7471,539.09851 L 128.09084,533.47348 L 124.65332,533.78598 L 121.2158,535.19224 L 121.68456,539.56726 L 122.77831,536.75475 L 123.71582,537.06725 L 122.30956,541.28602 L 125.43457,538.62976 L 126.05958,540.19226 L 122.30956,544.41103 L 121.05955,544.09853 L 120.5908,542.22352 L 119.3408,541.44227 L 118.09079,542.53603 L 115.43453,540.81727 L 112.46576,542.84853 L 110.74701,544.87979 L 107.46574,546.91105 L 102.93447,546.75479 L 102.46572,544.72354 L 106.05948,544.09853 L 106.05948,542.84853 L 103.87197,542.22352 L 104.80948,539.87976 L 106.99699,536.12975 L 106.99699,534.41099 L 107.15324,533.62973 L 111.37201,531.44222 L 112.30951,532.69223 L 114.96578,532.69223 L 113.71577,530.19222 L 110.122,529.87972 L 105.27823,532.53598 L 102.93447,535.81724 L 101.21571,538.31726 L 100.12196,540.50477 L 96.059441,541.91102 L 93.090671,544.41103 L 92.778171,545.97354 L 94.965681,546.91105 L 95.746941,548.9423 L 93.090671,552.06732 L 86.840651,556.12984 L 79.340608,560.19236 L 77.309348,561.28611 L 72.153076,562.37987 L 66.996796,564.56738 L 68.715556,565.81738 L 67.309296,567.22364 L 66.840546,568.31739 L 64.184286,567.37989 L 61.059276,567.53614 L 60.278016,569.72365 L 59.340516,569.72365 L 59.653016,567.37989 L 56.215496,568.6299 L 53.402986,569.5674 L 50.121721,568.31739 L 47.309208,570.1924 L 44.184194,570.1924 L 42.152934,571.44241 L 40.590427,572.22366 L 38.559168,571.91116 L 36.059156,570.81741 L 33.871646,571.44241 L 32.934142,572.37991 L 31.371634,571.28616 L 31.371634,569.41115 L 34.340398,568.16114 L 40.434176,568.78615 L 44.652946,567.22364 L 46.684205,565.19238 L 49.496718,564.56738 L 51.215476,563.78612 L 53.871739,563.94237 L 55.434246,565.19238 L 56.371746,564.87988 L 58.559256,562.22362 L 61.528026,561.28611 L 64.809286,560.66111 L 66.059296,560.34861 L 66.684296,560.81736 L 67.465556,560.81736 L 68.715556,557.22359 L 72.621826,555.81734 L 74.496836,552.22357 L 76.684348,547.84855 L 78.246858,546.44229 L 78.559358,543.94228 L 76.996848,545.19229 L 73.715576,545.81729 L 73.090576,543.47353 L 71.840576,543.16103 L 70.903066,544.09853 L 70.746816,546.91105 L 69.340556,546.75479 L 67.934306,541.12977 L 66.684296,542.37977 L 65.590546,541.91102 L 65.278046,540.03601 L 61.371776,540.19226 L 59.340516,541.28602 L 56.840506,540.97352 L 58.246756,539.56726 L 58.715506,537.06725 L 58.090506,535.19224 L 59.496766,534.25474 L 60.746766,534.09849 L 60.121766,532.37973 L 60.121766,528.16096 L 59.184266,527.22345 L 58.403006,528.62971 L 52.465482,528.62971 L 51.059226,527.3797 L 50.434223,523.62969 L 48.402963,520.19217 L 48.402963,519.25467 L 50.434223,518.47341 L 50.590473,516.44215 L 51.684228,515.3484 L 50.902975,514.87965 L 49.652969,515.3484 L 48.559214,512.69214 L 49.496718,507.84836 L 53.871739,504.72335 L 56.371746,503.16084 L 58.246756,499.56708 L 60.903026,498.31707 L 63.403036,499.41083 L 63.715536,501.75459 L 66.059296,501.44208 L 69.184306,499.09832 L 70.746816,499.72333 L 71.684316,500.34833 L 73.246826,500.34833 L 75.434338,499.09832 L 76.215598,494.87955 C 76.215598,494.87955 76.528098,492.06704 77.153098,491.59829 C 77.778098,491.12954 78.090598,490.66079 78.090598,490.66079 L 76.996848,488.78578 L 74.496836,489.56703 L 71.371816,490.34828 L 69.496806,489.87953 L 66.059296,488.16077 L 61.215526,488.00452 L 57.778006,484.41076 L 58.246756,480.66074 L 58.871766,478.31698 L 56.840506,476.59822 L 54.965494,473.00445 L 55.434246,472.2232 L 61.996776,471.75445 L 64.028036,471.75445 L 64.965536,472.69195 L 65.590546,472.69195 L 65.434296,471.12944 L 69.184306,470.50444 L 71.684316,470.81694 L 73.090576,471.9107 L 71.684316,473.94196 L 71.215566,475.34821 L 73.871836,476.91072 L 78.715608,478.62948 L 80.434368,477.69198 L 78.246858,473.47321 L 77.309348,470.34819 L 78.246858,469.56694 L 74.965588,467.69193 L 74.496836,466.59817 L 74.965588,465.03567 L 74.184336,461.28565 L 71.371816,456.75438 L 69.028056,452.69186 L 71.840576,450.81685 L 74.965588,450.81685 L 76.684348,451.44185 L 80.746868,451.2856 L 84.340631,447.84809 L 85.434391,444.87932 L 89.028161,442.53556 L 90.590661,443.47307 L 93.246921,442.84806 L 96.840691,440.8168 L 97.934451,440.66055 L 98.871951,441.44181 L 103.24697,441.28556 L 105.90323,438.31679 L 106.99699,438.31679 L 110.4345,440.66055 L 112.30951,442.69181 L 111.84076,443.78557 L 112.46576,444.87932 L 114.02827,443.31682 L 117.77829,443.62932 L 118.09079,447.22308 L 119.9658,448.62934 L 126.84083,449.25434 L 132.93461,453.31686 L 134.34086,452.37936 L 139.34089,454.87937 L 141.37215,454.25437 L 143.24716,453.47311 L 147.93468,455.34812 L 152.15345,458.16063 z M 40.902929,486.12951 L 42.934188,491.28579 L 42.777937,492.22329 L 39.965424,491.91079 L 38.246666,488.00452 L 36.527908,486.59827 L 34.184147,486.59827 L 34.027897,484.09825 L 35.746655,481.75449 L 36.84041,484.09825 L 38.246666,485.50451 L 40.902929,486.12951 z M 38.402917,518.47341 L 41.996684,519.25467 L 45.59045,520.19217 L 46.371704,521.12968 L 44.809197,524.72344 L 41.840433,524.56719 L 38.559168,521.12968 L 38.402917,518.47341 z M 18.402824,504.8796 L 19.49658,507.37961 L 20.590335,508.94212 L 19.49658,509.72337 L 17.46532,506.75461 L 17.46532,504.8796 L 18.402824,504.8796 z M 5.1215129,575.50493 L 8.4027779,573.31742 L 11.684043,572.37991 L 14.184055,572.69241 L 14.652807,574.25492 L 16.527816,574.72367 L 18.402824,572.84867 L 18.090323,571.28616 L 20.746585,570.66116 L 23.559098,573.16117 L 22.465343,574.87992 L 18.246574,575.97368 L 15.590311,575.50493 L 11.996545,574.41117 L 7.7777749,575.81743 L 6.2152679,576.12993 L 5.1215129,575.50493 z M 52.465482,571.12991 L 54.027989,573.00492 L 56.059246,571.44241 L 54.652992,570.1924 L 52.465482,571.12991 z M 55.277995,574.09867 L 56.371746,571.91116 L 58.403006,572.22366 L 57.621756,574.09867 L 55.277995,574.09867 z M 78.090598,572.22366 L 79.496858,573.94242 L 80.434368,572.84867 L 79.653108,570.97366 L 78.090598,572.22366 z M 86.528141,560.19236 L 87.621901,565.81738 L 90.434411,566.59864 L 95.278181,563.78612 L 99.496951,561.28611 L 97.934451,558.94235 L 98.403201,556.59859 L 96.371941,557.8486 L 93.559431,557.06734 L 95.121931,555.97359 L 96.996941,556.75484 L 100.74696,555.03608 L 101.21571,553.62983 L 98.871951,552.84857 L 99.653201,550.97356 L 96.996941,552.84857 L 92.465671,556.28609 L 87.778151,559.0986 L 86.528141,560.19236 z M 127.46583,540.97352 L 129.80959,539.56726 L 128.87209,537.8485 L 127.15333,538.78601 L 127.46583,540.97352 z " id="AK" style="fill:#f4d7d7" />
+ <ns0:g id="g16325" style="stroke:#000000;stroke-opacity:1">
+ <ns0:g id="g5778" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-opacity:1">
+ <ns0:path d="M 816.92419,258.09425 C 817.71859,258.70718 818.14466,259.56702 819.77271,260.56631 L 819.77271,260.61385" id="path6654" style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:1.33265233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" ns1:nodetypes="ccc" />
+ <ns0:g id="g4679" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-opacity:1">
+ <ns0:g id="g3580" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-opacity:1">
+ <ns0:g id="State_borders_old" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-opacity:1" transform="matrix(1.0566302,0,0,1.0756987,-45.325399,-166.80506)">
+ <ns0:path d="M 389.29574,462.33445 L 395.75915,462.99736 L 406.8077,463.54979" id="CO_OK" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 389.79293,462.72114 L 389.2405,473.88019" id="NM_OK" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 389.90342,473.82495 L 388.24613,473.82494 L 386.69931,491.94483 L 382.94283,545.64053 L 380.73312,568.62152 L 352.66979,567.07472 L 324.60654,564.42309 L 316.87248,563.76016 L 317.5354,568.62152" id="NM_TX" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 299.63678,367.31684 L 319.96612,369.74752 L 355.98408,373.72497 L 379.62831,375.71374" id="WY_CO" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 379.62831,375.71374 L 411.89008,378.36539 L 410.34328,400.02056" id="NE_CO" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 410.34328,400.02056 L 406.36581,463.66023" id="CO_KS" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 379.62831,375.71374 L 383.82676,332.84535" id="WY_NE" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 383.82676,332.84535 L 385.37355,308.31756 L 386.63467,290.78272" id="WY_SD" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 386.7128,291.15996 L 388.23277,275.63418 L 388.81756,270.31054" id="MT_SD" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 388.81755,270.85742 L 389.59881,259.06782 L 391.78171,234.95517 L 393.10754,220.37107 L 394.43337,206.67087" id="MT_ND" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 389.13006,270.75248 L 443.26797,274.28802 L 499.6156,275.83481" id="ND_SD" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 490.55578,211.09029 L 492.32355,216.17262 L 491.66064,219.92913 L 491.66064,229.87283 L 493.20744,234.73419 L 494.97521,238.04876 L 495.41715,247.55052 L 497.18492,260.58781 L 498.95269,267.65888 L 499.39463,275.83481" id="ND_MN" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 499.39463,275.83481 L 499.17366,278.92841 L 497.84783,280.25424 L 495.85909,281.80103 L 495.85909,283.34783 L 496.96395,285.1156 L 500.94143,288.43017 L 501.38337,291.30279 L 501.60434,320.69194 L 501.1624,327.32107" id="SD_MN" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 501.1624,327.32107 L 499.39463,327.32107 L 498.51074,329.75176 L 498.95269,332.40341 L 501.60434,334.39215 L 500.27851,339.91643 L 498.51074,344.11488 L 500.05754,346.76653 L 501.60434,348.97624" id="SD_IA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 501.60434,348.97624 L 499.83657,348.97624 L 498.0688,348.97624 L 497.18492,346.9875 L 496.74297,345.4407 L 493.87035,343.89391 L 489.00899,342.34711 L 487.24122,341.02128 L 484.58957,341.24225 L 480.83306,341.68419 L 476.63461,343.01002 L 475.52975,342.12614 L 470.88936,339.25351 L 468.90062,337.0438 L 458.51498,337.48574 L 435.53399,336.38089 L 418.29824,335.27603 L 398.85279,334.17118 L 383.82676,333.50826" id="SD_NE" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 500.94143,327.32107 L 512.21095,327.32107 L 560.16167,326.65816 L 577.61839,325.99525 L 581.59587,325.99525" id="MN_IA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 581.59587,325.99525 L 580.71198,318.7032 L 578.72324,316.05155 L 571.87314,312.07407 L 568.11663,306.54979 L 564.80207,306.10785 L 563.0343,303.01426 L 559.71973,303.01426 L 556.84711,300.14163 L 556.40516,293.73347 L 556.40516,289.97696 L 558.17293,286.88337 L 557.51002,283.78977 L 555.07934,281.80103 L 554.6374,281.58006 L 556.84711,275.83481 L 561.92944,272.07831 L 563.0343,270.53151 L 563.0343,262.35558 L 563.25527,259.70393 L 566.01741,256.8313" id="MN_WI" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 590.71092,259.59344 L 594.85413,264.78626 L 602.80909,265.89112 L 610.76405,268.10083 L 613.19473,269.20568 L 625.34814,271.85734 L 626.67397,274.06705 L 630.2095,275.1719 L 631.7563,285.1156 L 633.08213,286.44143 L 634.62893,287.60152" id="WI_MI" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 581.59587,325.77428 L 582.25878,329.08884 L 584.46849,330.63564 L 584.68946,331.96147 L 582.70072,335.27603 L 582.92169,338.36963 L 585.35238,342.12614 L 587.78306,343.23099 L 590.65568,343.67293 L 591.92627,346.3246" id="IA_WI" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 591.76054,345.88265 L 601.04136,346.10361 L 626.453,344.55682 L 635.95475,343.45196" id="WI_IL" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 591.76054,345.71692 L 591.98151,348.09236 L 594.19122,348.75527 L 595.0751,349.86012 L 595.51704,351.62789 L 599.27355,354.94246 L 599.93647,357.15217 L 599.27355,360.46674 L 597.50578,364.00227 L 596.84287,366.43295 L 594.63316,368.20072 L 592.86539,368.86364 L 587.78306,370.18946 L 587.12014,371.95723 L 586.45723,373.94597 L 587.12014,375.2718 L 588.88791,376.8186 L 588.66694,380.79607 L 586.89917,382.34287 L 586.23626,383.88967 L 586.23626,386.54132 L 584.46849,386.98326 L 582.92169,388.08812 L 582.70072,389.41395 L 582.92169,391.40269 L 581.15393,393.05996" id="IA_IL" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 581.3749,393.17045 L 578.06033,389.85589 L 576.95547,387.64618 L 569.44246,388.30909 L 559.9407,388.75103 L 535.41291,389.63492 L 522.37562,389.85589 L 513.31581,390.07686 L 511.98998,390.07686" id="IA_MO" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 512.53686,390.31124 L 510.66415,384.99453 L 510.44318,378.58636 L 508.89638,374.60888 L 508.23347,369.52655 L 506.02376,365.99101 L 505.13988,361.35062 L 502.48822,354.05857 L 501.1624,348.75527" id="NE_IA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 410.12231,399.79959 L 431.33554,400.68347 L 470.44713,402.00929 L 513.09483,402.45124 L 519.06105,402.45124" id="NE_KS" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 519.06105,402.45124 L 515.96746,398.47376 L 513.53678,394.71725 L 513.75775,392.50754 L 512.43192,390.07686" id="NE_MO" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 518.84008,402.45124 L 521.93368,405.10289 L 524.14339,405.32386 L 525.46921,406.20775 L 525.46921,409.08037 L 523.70145,410.62717 L 523.2595,412.83688 L 525.24824,416.15145 L 527.67893,419.02407 L 530.10961,420.79184 L 531.43543,432.06136 L 530.77252,466.53285" id="KS_MO" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 530.77252,466.53285 L 517.95614,467.19576 L 473.09935,466.75383 L 429.56781,464.76507 L 406.08959,463.43925" id="KS_OK" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 388.4119,473.88015 L 410.34328,474.92975 L 440.39535,476.03461 L 439.06952,499.0156 L 438.62758,516.25134 L 438.84855,517.79814 L 443.047,521.33368 L 445.03574,522.43853 L 445.69866,522.21756 L 446.36157,520.22882 L 447.6874,521.99659 L 449.67614,521.99659 L 449.67614,520.67076 L 452.32779,521.99659 L 451.88585,525.7531 L 455.86333,525.97407 L 458.29401,527.07893 L 462.27149,527.74184 L 464.70217,529.50961 L 466.91188,527.52087 L 470.22645,528.18378 L 472.65713,531.49835 L 473.54101,531.49835 L 473.54101,533.70806 L 475.75072,534.37097 L 477.96043,532.16126 L 479.7282,532.82417 L 482.15888,532.82417 L 483.04277,535.25486 L 487.68316,537.02262 L 489.00899,536.35971 L 490.77676,532.38223 L 491.88161,532.38223 L 492.98647,534.37097 L 496.96395,535.03388 L 500.49948,536.35971 L 503.37211,537.2436 L 505.13988,536.35971 L 505.80279,533.92903 L 510.00124,533.92903 L 511.98998,534.81291 L 514.64163,532.82417 L 515.74649,532.82417 L 516.4094,534.37097 L 520.38688,534.37097 L 521.93368,532.38223 L 523.70145,532.82417 L 525.69019,535.25486 L 528.78378,537.02262 L 531.87738,537.90651 L 534.52903,539.45331" id="OK_TX" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 534.52903,539.45331 L 534.30806,502.55125 L 532.98222,491.94454 L 532.98223,483.98957 L 531.43543,477.36043" id="OK_AR" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 531.43543,477.36043 L 530.99349,470.7313 L 530.77252,465.86994" id="OK_MO" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 531.43543,477.36043 L 553.97448,476.91849 L 576.51353,476.25558 L 596.6219,475.37169 L 607.22851,474.92975 L 608.99628,477.80238 L 608.55434,480.01209 L 605.46074,482.66374 L 604.79783,485.53636 L 610.76405,485.97831 L 615.62541,485.31539" id="MO_AR" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 615.62541,485.31539 L 617.61415,479.1282 L 617.61415,473.38295" id="MO_TN" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 617.33793,473.99063 L 620.26581,472.2781 L 621.59163,470.7313 L 623.58037,469.62645 L 623.80134,466.53285 L 624.68523,464.76508 L 623.13842,462.27915" id="MO_KY" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 623.58037,462.55537 L 620.48678,462.77634 L 618.71901,460.7876 L 617.17221,456.81012 L 618.05609,453.9375 L 616.06735,450.84391 L 614.07862,445.98254 L 609.65919,445.31963 L 603.472,440.23729 L 601.26229,436.48079 L 601.92521,433.38719 L 604.13492,427.64194 L 604.79783,424.10641 L 602.14618,422.78058 L 595.95899,422.33864 L 595.0751,421.01281 L 595.29607,416.81436 L 589.99277,413.27882 L 582.92169,405.98678 L 580.71198,398.9157 L 580.27004,395.38017 L 581.3749,392.28657" id="MO_IL" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 534.52903,538.79039 L 536.73874,541.0001 L 539.61136,539.67428 L 542.26302,540.77913 L 542.92593,551.38574" id="TX_AR" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 542.92593,551.38574 L 542.92593,560.8875 L 543.58884,569.94731 L 544.25176,573.70382 L 546.68244,577.6813 L 547.56632,582.54267 L 551.76477,587.84597 L 551.98574,590.93957 L 552.64866,591.60248 L 551.98574,599.77841 L 549.11312,604.63977 L 550.65992,606.62851 L 549.997,609.05919 L 549.33409,616.13027 L 548.00826,619.22386 L 548.28448,622.70416" id="TX_LA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 542.87069,551.88293 L 564.36012,551.60672 L 582.92169,550.72283 L 593.30733,550.72283" id="AR_LA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 593.52831,550.72283 L 593.74928,556.02614 L 595.73802,560.44556 L 595.51704,562.21333 L 597.72676,563.9811 L 597.06384,567.07469 L 595.95899,567.07469 L 596.84287,569.06343 L 591.76054,577.90227 L 588.44597,583.86849 L 587.78306,591.60248 L 588.225,593.14928 L 611.64793,592.48636 L 619.60289,591.82345 L 621.37066,591.60248 L 622.03357,592.48636 L 620.92872,599.77841 L 624.24328,602.872 L 625.34814,606.62851 L 626.61872,609.00395" id="LA_MS" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 593.41781,550.99905 L 594.79888,548.29215 L 594.41219,544.0937 L 593.08636,541.22107 L 594.41219,539.89525 L 593.08636,537.90651 L 593.52831,536.13874 L 594.41219,530.17252 L 597.28481,527.52087 L 596.6219,525.53213 L 600.15744,520.44979 L 602.80909,519.56591 L 602.80909,517.13523 L 602.14618,515.8094 L 604.79783,510.72707 L 607.44948,509.62221 L 607.44948,506.08667" id="AR_MS" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 642.50096,359.68676 L 643.28221,370.93678 L 644.53221,385.78055 L 645.78222,401.24932 L 646.25097,407.96808 L 645.46972,413.28059 L 645.78222,416.40559 L 647.50097,419.06185 L 647.96972,424.68686 L 645.46972,428.74936 L 643.75096,432.49937 L 641.56346,435.31187 L 641.09471,439.68688 L 641.09471,443.28063" id="IL_IN" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 641.09471,443.28063 L 640.15721,446.56189 L 639.06346,447.34314 L 639.68846,449.21814 L 640.46971,450.9369 L 638.90721,451.5619 L 636.4072,452.1869 L 634.5322,453.59315 L 634.2197,455.78065 L 635.4697,458.12441 L 635.31345,460.31191 L 634.2197,460.62441 L 630.78219,459.37441 L 628.12594,458.12441 L 626.09469,458.74941 L 623.90718,460.46816 L 623.12593,462.34316" id="IL_KY" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 641.25096,443.43688 L 644.06346,441.71813 L 649.68847,440.78063 L 653.12598,440.31188 L 654.53223,442.18688 L 656.25098,442.96813 L 657.96973,439.84313 L 660.78224,438.43688 L 662.65724,439.99938 L 663.43849,441.09313 L 665.46975,440.62438 L 665.31349,437.34313 L 668.126,435.78062 L 669.21975,434.99937 L 670.3135,436.56187 L 674.84476,436.56187 L 675.62601,434.53062 L 675.31351,432.34312 L 678.12601,428.90561 L 682.65727,425.15561 L 683.12602,420.7806 L 685.78228,420.4681 L 689.53228,418.74935 L 692.18854,416.87434 L 691.87603,414.99934 L 690.46978,413.59309 L 690.93853,411.40559" id="IN_KY" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 690.93853,411.40559 L 690.46978,405.93683 L 687.96978,383.28054 L 686.25103,369.99927 L 684.84477,355.7805" id="IN_OH" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 684.84477,355.7805 L 684.68852,354.68675 L 678.75102,355.31175 L 657.50098,357.49926 L 652.96973,357.49926" id="IN_MI" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 615.46967,485.07202 L 614.58577,488.10878 L 613.28217,492.34321 L 612.96967,494.84321 L 609.06341,497.03071 L 610.46966,500.46822 L 609.53216,504.99948 L 606.87591,506.09323" id="AR_TN" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 607.65716,506.56198 L 615.93842,506.24948 L 639.21971,504.37448 L 642.96971,504.21823" id="TN_MS" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 644.51654,503.77628 L 644.53221,510.31198 L 644.68846,526.40576 L 643.90721,556.4058 L 643.75096,569.99957 L 646.40722,588.1246 L 647.9524,603.33611" id="MS_AL" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 642.65721,504.21823 L 652.18848,503.74947 L 679.06352,501.24947 L 689.03673,500.46822" id="TN_AL" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 199.86827,240.25868 L 200.31021,231.08839 L 203.84575,215.17847 L 208.15468,195.07011 L 211.8007,182.03283 L 212.5741,178.27632" id="WA_ID" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 105.11516,210.08273 L 107.72336,210.64857 L 108.38627,213.07925 L 112.36375,213.63168 L 114.57346,217.38818 L 114.35249,224.9012 L 113.68958,225.6746 L 119.43482,229.65208 L 125.40104,229.98353 L 127.27929,228.21576 L 131.47774,229.65208 L 132.25114,229.54159 L 137.33348,231.53033 L 138.99076,232.85615 L 143.52066,233.07713 L 144.736,232.41421 L 147.0562,233.96101 L 151.36513,234.51344 L 156.66844,232.5247 L 157.11038,233.85052 L 172.02592,233.74004 L 186.61001,237.16509 L 200.38279,240.66827" id="WA_OR" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 199.97875,240.59014 L 201.19409,244.6781 L 204.50866,247.21927 L 204.28769,250.86529 L 200.53118,255.06374 L 196.77467,260.6985 L 195.89079,261.36141 L 195.33836,264.45501 L 194.23351,265.55986 L 192.46574,266.0018 L 188.1568,271.30511 L 187.82535,274.28822 L 187.38341,275.39307 L 188.1568,276.38744 L 190.58749,276.27696 L 191.80283,278.48667 L 189.37215,284.23191 L 188.04632,288.31988 L 183.84787,305.44513 L 179.53894,322.90184" id="OR_ID" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 134.1294,312.29523 L 126.28493,342.89971 L 119.54531,367.97992 L 117.96384,374.32659 L 129.93095,392.28673 L 151.91756,424.99044 L 170.7001,453.05375 L 184.73175,475.04037 L 187.28633,478.33598" id="CA_NV" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 179.25879,323.29247 L 199.53681,327.65271 L 208.92808,329.53097 L 217.8774,331.29873 L 225.72187,333.17699" id="ID_NV" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 225.5009,333.28747 L 237.87528,335.49718 L 249.47626,337.48592 L 259.41995,339.25369 L 267.48539,340.57952 L 272.23627,341.24243" id="ID_UT" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 187.05195,478.02348 L 186.61001,481.33804 L 189.26166,486.08892 L 190.477,491.72368 L 191.2504,492.71805 L 192.24477,493.27048 L 192.13428,495.48019 L 190.58749,496.80601 L 187.27292,498.46329 L 185.39467,500.34155 L 183.95836,503.87708 L 183.40593,508.62796 L 180.64379,511.27961 L 178.65505,511.94253 L 178.54457,517.57729 L 178.10262,519.23457 L 178.54457,520.00797 L 182.0801,520.56039 L 181.52767,523.21205 L 180.09136,525.31127 L 176.44534,526.19515" id="CA_AZ" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 187.27292,478.24445 L 188.59875,476.03474 L 189.15117,463.88133 L 189.37215,462.22405 L 189.48263,455.48444 L 191.36088,454.49007 L 192.24477,453.93764 L 193.12865,453.93764 L 194.01254,455.04249 L 196.66419,455.37395 L 197.87953,458.0256 L 200.31021,458.13609 L 201.96749,455.59492 L 202.40943,455.15298 L 205.40595,437.74758" id="NV_AZ" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 205.28206,438.35918 L 207.16031,427.86306 L 210.2539,413.16849 L 213.45798,397.14809 L 215.55721,384.00032 L 217.43546,375.38245 L 221.08148,355.60554 L 224.50653,338.70126 L 225.61139,333.50844" id="NV_UT" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 70.710722,294.61753 L 77.560823,296.82724 L 88.609373,299.69986 L 95.901416,301.6886 L 108.05482,305.66608 L 121.09211,308.98065 L 134.1294,312.73715" id="OR_CA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 133.68746,312.68584 L 166.39117,320.47114 L 179.64943,323.34376" id="OR_NV" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 226.27435,180.81733 L 221.85493,201.58861 L 225.1695,208.88065 L 223.84367,213.30007 L 225.61144,217.71949 L 228.70504,219.04532 L 232.24057,228.98902 L 235.77611,232.52455 L 236.21805,233.62941 L 239.53262,234.73427 L 239.97456,236.723 L 233.12446,253.73778 L 233.12446,256.16846 L 235.55514,259.26205 L 236.43902,259.26205 L 241.07941,256.38943 L 241.74233,255.28457 L 243.28913,255.94749 L 243.06815,261.02982 L 245.71981,273.18323 L 248.59243,275.61391 L 249.47631,276.27682 L 251.24408,278.48653 L 250.80214,281.8011 L 251.46505,285.11566 L 252.56991,285.99955 L 254.77962,283.78984 L 257.43127,283.78984 L 260.52487,285.33664 L 262.95555,284.45275 L 266.93303,284.45275 L 270.46856,285.99955 L 273.12022,285.55761 L 273.56216,282.68498 L 276.43478,282.02207 L 277.76061,283.3479 L 278.20255,286.44149 L 280.63323,288.6512" id="ID_MT" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 280.76267,289.04183 L 282.18003,277.82362 L 301.40451,280.69624 L 328.80492,284.67372 L 344.49387,286.66246 L 375.50794,289.84759 L 386.47836,290.86091" id="MT_WY" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 280.8542,288.20926 L 277.0977,312.07413 L 272.01536,341.46328" id="ID_WY" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 272.01536,341.46328 L 270.5356,351.6275 L 268.92177,363.11844 L 275.2273,364.01574 L 291.55004,366.22545 L 300.53404,367.40842" id="UT_WY" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 300.29966,367.31689 L 297.42703,388.75109 L 294.33344,410.40625 L 290.70637,437.45625 L 289.2511,448.1923 L 288.58819,452.16978" id="UT_CO" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 205.06113,437.58569 L 237.54388,443.77288 L 263.83943,448.1923 L 288.80916,451.85728" id="UT_AZ" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 288.58819,451.94881 L 284.5839,481.67727 L 277.85214,533.09881 L 274.22507,559.11977 L 272.4573,571.93609" id="AZ_NM" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 288.80916,451.72784 L 321.95482,455.92629 L 357.65689,460.20517 L 389.35099,462.33445" id="CO_NM" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 685.15727,355.93675 L 692.50104,354.68675 L 702.3448,353.12425 L 707.42849,352.54501" id="MI_OH" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 691.09478,411.56184 L 695.00104,411.24934 L 697.34479,410.46809 L 700.1573,412.03059 L 701.7198,416.24934 L 707.34481,416.56184 L 709.06356,418.2806 L 711.09481,418.43685 L 713.43857,417.0306 L 716.40732,417.49935 L 717.65732,418.9056 L 720.31358,416.40559 L 722.03233,415.15559 L 723.59483,415.15559 L 724.21983,417.81185 L 725.93859,418.74935 L 729.37609,420.7806" id="OH_KY" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 729.37609,420.7806 L 729.53234,426.09311 L 730.31359,427.65561 L 732.8136,429.06186 L 733.4386,431.24937 L 736.2511,434.84312 L 738.7511,437.49938 L 741.92187,439.62379" id="KY_WV" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 742.03236,438.90563 L 738.90736,442.65563 L 734.84485,446.09314 L 730.46984,451.2494 L 728.75109,452.96815 L 728.75109,454.9994 L 725.00108,457.03065 L 719.53233,460.31191 L 716.70413,461.72601" id="KY_VA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 717.31181,461.61552 L 666.876,466.40567 L 651.64388,468.12442 L 647.17734,468.61997 L 643.43846,468.59317 L 643.43846,472.34318 L 635.31345,472.81193 L 628.59469,473.43693 L 618.12592,473.59318" id="KY_TN" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 665.15724,603.28087 L 664.84474,595.46836 L 662.34474,593.59336 L 660.62599,591.87461 L 660.93849,588.90585 L 670.78225,587.65585 L 695.46979,584.84335 L 702.0323,584.21835 L 708.12606,584.21835" id="AL_FL" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 708.12606,584.21835 L 707.34481,580.78084 L 705.93856,579.99959 L 705.31355,579.21834 L 706.09481,576.87458 L 705.78231,571.71833 L 704.2198,567.49957 L 704.37605,565.62457 L 704.8448,562.49956 L 706.56356,557.81206 L 706.40731,555.7808 L 704.5323,554.99955 L 704.06355,551.7183 L 701.56355,548.43704 L 699.5323,542.34328 L 698.12604,535.62452 L 696.56354,530.93702 L 695.15729,524.99951 L 692.81354,515.46824 L 689.53228,507.81198 L 688.90728,504.53073 L 688.75103,502.49947 L 688.75103,500.31197" id="AL_GA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 707.81356,583.90584 L 710.46981,587.8121 L 711.87606,589.21835 L 719.53233,589.3746 L 729.98996,588.7496 L 750.78237,587.4996 L 756.04583,586.8478 L 760.46989,586.8746 L 760.62614,589.6871 L 763.12614,590.46835 L 763.43864,586.2496 L 761.87614,581.87459 L 762.96989,580.31209 L 768.5949,581.09334 L 773.59491,581.40584" id="GA_FL" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 688.75103,500.46822 L 697.03229,499.53072 L 705.1573,498.43697 L 709.84481,497.65572" id="TN_GA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 709.84481,497.65572 L 716.40732,497.18696 L 723.12608,496.40571 L 728.75109,495.46821 L 730.46984,495.31196" id="NC_GA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 730.46984,495.31196 L 730.62609,497.18696 L 728.43859,498.12447 L 727.65734,499.99947 L 727.96984,501.71822 L 729.21984,502.96822 L 733.1261,505.31198 L 736.40735,505.15573 L 739.68861,510.15573 L 740.15736,511.56199 L 742.50111,514.37449 L 742.96986,515.78074 L 747.34487,517.4995 L 750.31362,519.687 L 752.50113,522.4995 L 754.68863,523.7495 L 756.71988,525.62451 L 757.96988,528.12451 L 760.00114,529.99951 L 764.06364,531.87452 L 766.7199,537.65578 L 768.2824,542.34328 L 770.7824,542.96828 L 772.96991,544.99954 L 774.21991,548.43704 L 774.84491,550.46829 L 777.34491,551.7183 L 779.37617,550.7808" id="GA_SC" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 730.15734,494.99946 L 736.2511,492.65571 L 745.00111,488.2807 L 752.03237,487.49945 L 767.9699,487.0307 L 770.1574,488.9057 L 771.7199,492.03071 L 775.93866,491.56196 L 788.12618,490.1557 L 790.93868,490.93696 L 803.1262,498.28072 L 812.97945,506.32368" id="NC_SC" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 709.84481,497.65572 L 710.15731,492.96821 L 711.87606,491.56196 L 714.53232,490.93696 L 715.15732,487.3432 L 719.21983,484.68695 L 722.96983,483.28069 L 727.03234,479.84319 L 731.25109,477.81194 L 731.87609,474.84318 L 735.6261,471.09318 L 736.2511,470.93693 C 736.2511,470.93693 736.2511,472.03068 737.03235,472.03068 C 737.8136,472.03068 738.90736,472.34318 738.90736,472.34318 L 741.09486,468.90567 L 743.12611,468.28067 L 745.31361,468.59317 L 746.87612,465.15567 L 749.68862,462.65566 L 750.15737,460.62441 L 750.31362,456.71815" id="TN_NC" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 717.03232,461.56191 L 726.53223,460.74273 L 738.1261,458.90566 L 745.78237,458.74941 L 748.12612,456.8744 L 750.34206,456.92964" id="VA_TN" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 750.00112,456.8744 L 754.53238,457.49941 L 761.42964,456.2494 L 776.71991,454.3744 L 793.28244,451.8744 L 813.19549,448.22704 L 831.71999,444.53064 L 842.81376,441.71813 L 847.56599,440.14615" id="VA_NC" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 741.65514,439.21813 L 743.90736,442.34313 L 745.15736,443.59313 L 747.96987,444.37439 L 750.46987,443.74938 L 752.81363,441.56188 C 752.81363,441.56188 754.53238,442.96813 755.15738,442.81188 C 755.78238,442.65563 759.37614,441.87438 759.37614,441.87438 L 761.09489,437.34313 L 763.59489,438.28063 L 766.7199,435.93687 L 768.12615,436.24937 L 770.1574,434.37437 L 770.31365,431.71812 L 769.5324,430.46812 L 774.37616,419.99935 L 776.40741,413.59309 L 776.71991,409.37433 L 778.43866,409.21808 L 780.00117,411.87434 L 781.25117,412.65559 L 783.75117,412.65559 L 784.84492,407.34308 L 785.15742,404.37433 L 788.43868,404.06183 L 788.90743,401.87432 L 791.87618,399.53057 L 792.50119,397.96807 L 794.06369,395.31181 L 794.53244,393.28056 L 794.68869,388.1243 L 799.06369,389.84305 L 804.53246,392.81181 L 805.46995,387.65555" id="WV_VA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 805.15745,388.1243 L 809.37621,389.84305 L 809.37621,392.65556 L 814.68872,393.90556 L 816.56372,395.15556 L 817.50122,393.28056 L 819.68873,394.84306 L 818.28247,397.96807 L 817.96997,400.62432 L 816.25122,403.12432 L 816.25122,405.15558 L 816.87622,406.87433 L 822.13512,408.2443" id="VA_MD" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 805.6262,388.28055 L 804.21995,386.71805 L 802.34495,384.9993 L 799.53245,383.59305 L 797.94314,382.50092 L 796.15966,382.96967 L 794.21994,384.3743 L 791.71993,386.40555 L 788.90743,386.71805 L 787.65743,386.09305 L 785.93868,388.59305 L 784.53242,389.9993 L 782.18867,390.15555 L 780.00117,393.12431 L 777.96991,395.46806 L 777.65741,395.78056 L 776.71991,390.31181 L 775.31366,385.3118" id="WV_MD" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 775.64512,385.53277 L 763.62333,387.03055 L 759.14731,387.75656 L 755.60717,368.59302" id="PA_WV" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 755.93863,368.59302 L 754.53238,369.84302 L 753.59488,371.24928 L 754.68863,374.21803 L 754.68863,378.74929 L 754.21988,383.59305 L 753.90738,389.0618 L 751.71987,392.81181 L 748.75112,396.09306 L 746.56362,397.65557 L 744.53236,397.18682 L 743.28236,398.59307 L 741.09486,401.87432 L 740.15736,403.12432 L 740.15736,405.46808 L 741.25111,407.18683 L 740.78236,408.74933 L 739.06361,409.68683 L 738.59485,407.96808 L 737.34485,406.87433 L 736.09485,407.49933 L 735.15735,411.24934 L 735.0011,416.09309 L 733.4386,417.49935 L 733.28235,420.1556 L 731.40734,420.93685 L 729.21984,421.24935" id="OH_WV" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 751.25169,340.03513 L 754.06419,358.74889 L 755.78295,369.68641" id="OH_PA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 761.7015,331.46032 L 762.34546,338.28009 L 770.93923,336.87384 L 812.50182,328.43632 L 830.47061,324.53006 L 832.72285,326.59924 L 835.62687,327.49882 L 837.97063,332.65508 L 840.15813,334.53008 L 842.65814,334.68633 L 843.90814,335.78009" id="NY_PA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 843.75189,335.62383 L 847.34565,337.34259 L 851.09566,338.43634 L 855.47067,339.37384 L 857.97067,340.4676 L 858.12692,342.49885 L 857.65817,345.15511 L 858.24689,348.66681" id="NY_NJ" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 844.06439,335.46758 L 841.72064,337.96759 L 841.72064,340.93635 L 839.84563,343.9051 L 839.68938,345.46761 L 840.93939,346.71761 L 840.78314,349.06137 L 838.59563,350.15512 L 839.37688,352.81137 L 839.53313,353.90513 L 842.18939,354.21763 L 843.12689,356.71763 L 846.5644,359.06139 L 848.90815,360.62389 L 848.90815,361.40514 L 845.78315,364.3739 L 844.22064,366.5614 L 842.81439,369.21766 L 840.62689,370.46766 L 839.53313,371.24891" id="PA_NJ" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 839.37688,371.09266 L 839.22063,372.34267 L 838.66983,374.88059" id="DE_NJ" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 839.53313,371.09266 L 837.50188,371.09266 L 835.47062,372.65517 L 834.06437,374.06142" id="NY_DE" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 834.06437,374.06142 L 835.47062,378.12393 L 837.65813,383.59269 L 839.68938,392.96771 L 841.25189,399.06148 L 846.09565,398.90523 L 852.03316,397.81147" id="MD_DE" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 834.38635,373.6842 L 826.20281,375.44087 L 811.82448,378.1697 L 774.68924,385.62395" id="PA_MD" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 862.06162,339.46537 L 860.15818,337.34259 L 861.25193,335.15508 L 861.25193,327.34257 L 859.84568,320.3113 L 859.06443,316.87379" id="NY_CT" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 859.06443,316.87379 L 858.28318,311.56128 L 858.75193,301.09251" id="NY_MA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 858.75193,301.09251 L 858.28318,296.0925 L 855.31442,285.46747 L 854.68942,285.15497 L 851.87691,283.90497 L 852.65816,281.09246 L 851.87691,279.06121 L 849.37691,274.6862 L 850.31441,270.93619 L 849.53316,265.93618 L 847.1894,259.68616 L 846.5644,254.8424" id="NY_VT" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 872.65821,247.81114 L 872.97071,253.5924 L 874.84571,256.24866 L 874.84571,260.15492 L 871.25195,264.06117 L 868.75195,265.15493 L 868.75195,266.24868 L 869.8457,267.96743 L 869.8457,276.2487 L 869.06445,285.15497 L 868.9082,289.84248 L 869.8457,291.09249 L 869.68945,295.46749 L 869.2207,297.18625 L 870.7832,299.06125" id="VT_NH" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 859.06443,301.56126 L 863.90819,300.46751 L 870.4707,299.2175" id="VT_MA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 870.4707,299.2175 L 887.34574,295.15499 L 889.53325,294.52999 L 891.5645,291.40499 L 895.50868,289.5947" id="NH_MA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 896.48246,285.35698 L 894.06451,284.21747 L 893.59575,281.24871 L 889.84575,280.15496 L 889.53325,277.4987 L 882.50198,254.8424 L 877.81447,240.46737" id="NH_ME" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 859.37693,317.34254 L 880.31447,312.49878 L 885.31449,311.40503" id="MA_CT" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 885.31449,311.40503 L 886.87699,317.18629 L 887.65824,321.40505 L 888.28324,325.46756" id="CT_RI" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ <ns0:path d="M 885.15824,311.56128 L 890.78325,309.99878 L 892.34575,311.09253 L 895.62701,315.31129 L 898.43952,319.6863" id="RI_MA" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:1pt;stroke-opacity:1" />
+ </ns0:g>
+ <ns0:g id="g3561" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-opacity:1">
+ <ns0:path d="M 242.11104,448.52821 L 258.12559,450.54515 L 259.44637,440.12432 L 276.45152,442.81356 L 290.31977,444.66242" id="NM_Mexico" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ <ns0:path d="M 289.98957,444.32625 L 292.13584,444.99855 L 294.77744,448.02394 L 296.26332,452.56207 L 301.05119,454.91517 L 302.37198,458.27673 L 309.63631,466.51255 L 310.9571,468.19333 L 316.07515,470.37834 L 317.23084,472.56336 L 318.88182,473.57182 L 319.37712,476.42915 L 322.67909,483.15227 L 322.67909,491.55616 L 324.99047,496.43042 L 332.585,504.49816 L 337.86815,506.68317 L 339.68423,508.70011 L 339.68423,509.37242 L 343.64659,511.72551 L 345.62777,512.39782 L 347.44386,513.57437 L 350.08543,514.58284 L 352.56191,512.06167 L 357.01957,505.67471 L 358.01016,501.80891 L 360.32154,498.44736 L 363.9537,496.93466 L 368.57646,495.0858 L 371.71333,497.43889 L 379.30786,498.1112 L 386.242,499.28775 L 388.88357,501.47276 L 388.88357,502.6493 L 391.52515,505.84279 L 397.63379,511.38936 L 397.79889,512.90206 L 399.61497,514.91899 L 400.44047,519.28902 L 405.88872,532.06294 L 405.72362,534.07988 L 410.01618,536.76912 L 413.64834,543.66032 L 417.11541,548.19842 L 420.41738,549.54304 L 422.06837,551.89614 L 420.74758,556.43424 L 421.40797,557.44271 L 422.72876,558.11502 L 422.39856,561.64466 L 421.73817,562.31697 L 422.39856,564.67006 L 425.70053,566.68699 L 427.02132,573.41011 L 429.1676,577.44398 L 436.92723,580.97362 L 442.21038,582.15016 L 446.50294,585.34364 L 449.80491,586.01595 L 451.1257,585.51172 L 456.73904,586.68827 L 462.51749,590.72214 L 465.65436,588.7052" id="TX_Mexico" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ <ns0:path d="M 95.503595,393.0625 L 105.7397,394.5752 L 125.88171,397.43253 L 140.74058,399.1133" id="CA_Mexico" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ <ns0:path d="M 140.74058,399.1133 L 138.4292,401.4664 L 138.099,402.9791 L 138.5943,403.98756 L 157.91082,415.08071 L 170.2932,422.98037 L 185.31716,431.8885 L 202.4874,442.30933 L 215.03489,444.8305 L 242.11104,448.52821" id="AZ_Mexico" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ <ns0:g id="g3547" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-opacity:1">
+ <ns0:path d="M 876.74955,100.10268 L 848.85548,107.51359" id="VT_Canada" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ <ns0:path d="M 943.28423,76.73985 L 940.47756,76.907928 L 940.97285,78.084474 L 940.80775,78.588708 L 939.98226,78.588708 L 937.01049,76.235617 L 936.84539,71.193279 L 936.0199,69.176344 L 929.91126,69.176344 L 920.66574,38.081928 L 918.68456,37.073461 L 912.57592,34.552292 L 911.09003,34.384214 L 909.27395,36.233071 L 905.14649,39.258474 L 905.14649,40.266941 L 904.32099,41.107331 L 901.51432,40.435019 L 900.19353,38.081928 L 900.19353,36.905383 L 898.87274,36.737305 L 897.55196,36.737305 L 895.40568,41.107331 L 892.4339,50.351617 L 890.61782,55.393954 L 890.78292,60.436292 L 890.94802,61.948993 L 890.12252,64.806318 L 889.29703,65.814786 L 889.29703,72.033669 L 891.27821,74.554837 L 889.79233,78.756786 L 887.15075,83.631045 L 886.32526,89.345695 L 886.32526,92.034941 L 884.77927,91.608771 L 881.71077,91.733617" id="ME_Canada" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ <ns0:path d="M 882.59051,91.36264 L 881.5374,92.203019 L 880.87701,93.883799 L 880.21662,93.379565 L 879.22603,92.371097 L 877.74014,94.388032 L 875.84148,100.77501" id="NH_Canada" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ <ns0:g id="g3537" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-opacity:1">
+ <ns0:path d="M 710.49539,188.67975 L 711.48598,189.52014 L 710.49539,190.69668 L 710.66049,191.36899 L 711.48598,191.53707 L 713.46716,190.5286 L 713.13697,180.61201 M 704.88204,204.47907 L 704.88204,198.9325 L 706.86322,196.91556 L 707.68872,196.57941" id="MI_Canada" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ <ns0:path d="M 766.79397,165.82115 L 768.77515,172.20811 L 770.59123,172.37619 L 771.91202,175.56967 M 849.4392,107.39474 L 841.58358,109.51505 L 836.96082,111.02775 L 833.65885,110.85967 L 828.0455,112.20429 L 824.7235,113.61855" id="NY_Canada" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-opacity:1" />
+ <ns0:g id="g3530" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-opacity:1">
+ <ns0:path d="M 179.70368,24.971818 L 163.02887,21.106058 L 139.58489,15.223331 L 119.11268,9.3406038 L 110.36246,7.3236687 L 100.45655,4.4663441 L 95.99889,2.9536428" id="WA_Canada" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ <ns0:path d="M 193.57209,27.997253 L 179.20868,25.139971" id="ID_Canada" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ <ns0:path d="M 371.21804,55.393954 L 338.69364,52.032396 L 308.81082,48.334682 L 278.92799,44.132734 L 245.9083,38.586162 L 227.08707,35.056526 L 193.57209,27.997253" id="MT_Canada" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ <ns0:path d="M 472.75352,59.76398 L 442.87077,59.427825 L 423.88445,58.755513 L 396.8083,57.410889 L 371.21804,55.393954" id="ND_Canada" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ <ns0:path d="M 590.4688,78.756786 L 585.35075,79.261019 L 584.85546,80.269487 L 584.19506,80.269487 L 582.37898,77.076006 L 573.29856,77.412162 L 572.30797,78.252552 L 571.31738,78.252552 L 570.82209,76.907928 L 569.99659,75.059071 L 567.35502,75.563305 L 564.05305,78.924863 L 562.40206,79.765253 L 559.26519,79.765253 L 556.62362,78.756786 L 556.62362,76.571773 L 555.30283,76.403695 L 554.80753,76.907928 L 552.16596,75.563305 L 551.67066,72.537902 L 550.18478,73.042136 L 549.68948,74.050604 L 547.21301,73.54637 L 541.76476,71.025201 L 537.80239,68.335954 L 534.83062,68.335954 L 533.50983,67.327487 L 531.19845,67.999799 L 530.04276,69.176344 L 529.71257,70.520967 L 524.75961,70.520967 L 524.75961,68.335954 L 518.32077,67.999799 L 517.99058,66.487097 L 513.03762,66.487097 L 511.38664,64.806318 L 509.90075,58.419357 L 509.07526,52.704708 L 507.09408,51.864318 L 504.7827,51.360084 L 504.1223,51.528162 L 503.79211,60.100136 L 472.09313,60.100136" id="MN_Canada" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:2.66530466;stroke-dasharray:none;stroke-opacity:1" />
+ </ns0:g>
+ </ns0:g>
+ </ns0:g>
+ </ns0:g>
+ </ns0:g>
+ <ns0:g id="g4675" style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-opacity:1">
+ <ns0:path d="M 846.52085,274.72594 L 847.80814,273.69012 L 850.29359,272.77823 L 852.1404,272.10064 L 852.89317,271.82446 L 853.40684,271.87199" id="path3995" style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:1.33265233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" ns1:nodetypes="cccccc" />
+ <ns0:path d="M 817.25107,257.90409 C 818.2317,258.85489 818.79206,259.61552 818.79206,259.61552 L 819.39913,260.85156 L 819.53922,260.70894" id="path5767" style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:0.41898587;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" ns1:nodetypes="cccc" />
+ </ns0:g>
+ </ns0:g>
+ </ns0:g>
+ <ns0:path d="M 152.15345,458.16063 L 151.84095,540.66102 L 153.40345,541.59852 L 156.37222,541.75477 L 157.77847,540.66102 L 160.27848,540.66102 L 160.43474,543.47353 L 167.15352,550.03606 L 167.62227,552.53607 L 170.90353,550.66106 L 171.52854,550.50481 L 171.84104,547.53605 L 173.24729,545.97354 L 174.34105,545.81729 L 176.21606,544.41103 L 179.18482,546.44229 L 179.80983,549.25481 L 181.68483,550.34856 L 182.77859,552.69232 L 186.52861,554.41108 L 189.80987,560.19236 L 192.46613,563.94237 L 194.65364,566.59864 L 196.0599,570.1924 L 200.90367,571.91116 L 205.9037,573.94242 L 206.8412,578.16119 L 207.30995,581.12995 L 206.37245,584.41122 L 204.34118,587.14562" id="AK_Canada" style="fill:none;stroke:#000000;stroke-width:2.5;stroke-dasharray:none;stroke-opacity:1" />
+ </ns0:g>
+ <ns0:path d="M 127.46583,540.97352 L 129.80959,539.56726 L 128.87209,537.8485 L 127.15333,538.78601 L 127.46583,540.97352 z M 86.528141,560.19236 L 87.621901,565.81738 L 90.434411,566.59864 L 95.278181,563.78612 L 99.496951,561.28611 L 97.934451,558.94235 L 98.403201,556.59859 L 96.371941,557.8486 L 93.559431,557.06734 L 95.121931,555.97359 L 96.996941,556.75484 L 100.74696,555.03608 L 101.21571,553.62983 L 98.871951,552.84857 L 99.653201,550.97356 L 96.996941,552.84857 L 92.465671,556.28609 L 87.778151,559.0986 L 86.528141,560.19236 z M 78.090601,572.22366 L 79.496861,573.94242 L 80.434371,572.84867 L 79.653111,570.97366 L 78.090601,572.22366 z M 55.278,574.09867 L 56.371751,571.91116 L 58.403011,572.22366 L 57.621761,574.09867 L 55.278,574.09867 z M 52.465487,571.12991 L 54.027994,573.00492 L 56.059251,571.44241 L 54.652997,570.1924 L 52.465487,571.12991 z M 5.1215179,575.50493 L 8.4027829,573.31742 L 11.684048,572.37991 L 14.18406,572.69241 L 14.652812,574.25492 L 16.527821,574.72367 L 18.402829,572.84867 L 18.090328,571.28616 L 20.74659,570.66116 L 23.559103,573.16117 L 22.465348,574.87992 L 18.246579,575.97368 L 15.590316,575.50493 L 11.99655,574.41117 L 7.7777799,575.81743 L 6.2152729,576.12993 L 5.1215179,575.50493 z M 18.402829,504.8796 L 19.496585,507.37961 L 20.59034,508.94212 L 19.496585,509.72337 L 17.465325,506.75461 L 17.465325,504.8796 L 18.402829,504.8796 z M 38.402922,518.47341 L 41.996689,519.25467 L 45.590455,520.19217 L 46.371709,521.12968 L 44.809202,524.72344 L 41.840438,524.56719 L 38.559173,521.12968 L 38.402922,518.47341 z M 40.902934,486.12951 L 42.934193,491.28579 L 42.777942,492.22329 L 39.965429,491.91079 L 38.246671,488.00452 L 36.527913,486.59827 L 34.184152,486.59827 L 34.027902,484.09825 L 35.74666,481.75449 L 36.840415,484.09825 L 38.246671,485.50451 L 40.902934,486.12951 z M 204.65369,586.59873 L 203.09118,585.81748 L 201.68493,582.84871 L 199.02866,581.44246 L 197.30991,580.3487 L 196.52865,581.12995 L 197.93491,583.78622 L 198.09116,587.37998 L 196.9974,587.84873 L 195.1224,585.97373 L 193.09114,584.72372 L 193.55989,586.28623 L 194.80989,588.00499 L 194.02864,588.78624 C 194.02864,588.78624 193.24739,588.47374 192.77864,587.84873 C 192.30988,587.22373 190.74738,584.56747 190.74738,584.56747 L 189.80987,582.37996 C 189.80987,582.37996 189.49737,583.62997 188.87237,583.31746 C 188.24736,583.00496 187.62236,581.91121 187.62236,581.91121 L 189.34112,580.0362 L 187.93486,578.62994 L 187.93486,573.78617 L 187.15361,573.78617 L 186.37236,577.06743 L 185.2786,577.53619 L 184.3411,573.94242 L 183.71609,570.34865 L 182.93484,569.8799 L 183.24734,575.34868 L 183.24734,576.44243 L 181.84108,575.19243 L 178.40357,569.41115 L 176.37231,568.9424 L 175.74731,565.34863 L 174.1848,562.53612 L 172.62229,561.44236 L 172.62229,559.25485 L 174.65355,558.00485 L 174.1848,557.69235 L 171.68479,558.31735 L 168.40352,555.97359 L 165.90351,553.16107 L 161.21599,550.66106 L 157.30972,548.16105 L 158.55973,545.03604 L 158.55973,543.47353 L 156.84097,545.03604 L 154.02846,546.12979 L 150.43469,545.03604 L 144.96591,542.69228 L 139.65339,542.69228 L 139.02839,543.16103 L 132.77836,539.41101 L 130.7471,539.09851 L 128.09084,533.47348 L 124.65332,533.78598 L 121.2158,535.19224 L 121.68456,539.56726 L 122.77831,536.75475 L 123.71582,537.06725 L 122.30956,541.28602 L 125.43457,538.62976 L 126.05958,540.19226 L 122.30956,544.41103 L 121.05955,544.09853 L 120.5908,542.22352 L 119.3408,541.44227 L 118.09079,542.53603 L 115.43453,540.81727 L 112.46576,542.84853 L 110.74701,544.87979 L 107.46574,546.91105 L 102.93447,546.75479 L 102.46572,544.72354 L 106.05948,544.09853 L 106.05948,542.84853 L 103.87197,542.22352 L 104.80948,539.87976 L 106.99699,536.12975 L 106.99699,534.41099 L 107.15324,533.62973 L 111.37201,531.44222 L 112.30951,532.69223 L 114.96578,532.69223 L 113.71577,530.19222 L 110.122,529.87972 L 105.27823,532.53598 L 102.93447,535.81724 L 101.21571,538.31726 L 100.12196,540.50477 L 96.059441,541.91102 L 93.090671,544.41103 L 92.778171,545.97354 L 94.965681,546.91105 L 95.746941,548.9423 L 93.090671,552.06732 L 86.840651,556.12984 L 79.340611,560.19236 L 77.309351,561.28611 L 72.153081,562.37987 L 66.996801,564.56738 L 68.715561,565.81738 L 67.309301,567.22364 L 66.840551,568.31739 L 64.184291,567.37989 L 61.059281,567.53614 L 60.278021,569.72365 L 59.340521,569.72365 L 59.653021,567.37989 L 56.215501,568.6299 L 53.402991,569.5674 L 50.121726,568.31739 L 47.309213,570.1924 L 44.184199,570.1924 L 42.152939,571.44241 L 40.590432,572.22366 L 38.559173,571.91116 L 36.059161,570.81741 L 33.871651,571.44241 L 32.934147,572.37991 L 31.371639,571.28616 L 31.371639,569.41115 L 34.340403,568.16114 L 40.434181,568.78615 L 44.652951,567.22364 L 46.68421,565.19238 L 49.496723,564.56738 L 51.215481,563.78612 L 53.871744,563.94237 L 55.434251,565.19238 L 56.371751,564.87988 L 58.559261,562.22362 L 61.528031,561.28611 L 64.809291,560.66111 L 66.059301,560.34861 L 66.684301,560.81736 L 67.465561,560.81736 L 68.715561,557.22359 L 72.621831,555.81734 L 74.496841,552.22357 L 76.684351,547.84855 L 78.246861,546.44229 L 78.559361,543.94228 L 76.996851,545.19229 L 73.715581,545.81729 L 73.090581,543.47353 L 71.840581,543.16103 L 70.903071,544.09853 L 70.746821,546.91105 L 69.340561,546.75479 L 67.934311,541.12977 L 66.684301,542.37977 L 65.590551,541.91102 L 65.278051,540.03601 L 61.371781,540.19226 L 59.340521,541.28602 L 56.840511,540.97352 L 58.246761,539.56726 L 58.715511,537.06725 L 58.090511,535.19224 L 59.496771,534.25474 L 60.746771,534.09849 L 60.121771,532.37973 L 60.121771,528.16096 L 59.184271,527.22345 L 58.403011,528.62971 L 52.465487,528.62971 L 51.059231,527.3797 L 50.434228,523.62969 L 48.402968,520.19217 L 48.402968,519.25467 L 50.434228,518.47341 L 50.590478,516.44215 L 51.684233,515.3484 L 50.90298,514.87965 L 49.652974,515.3484 L 48.559219,512.69214 L 49.496723,507.84836 L 53.871744,504.72335 L 56.371751,503.16084 L 58.246761,499.56708 L 60.903031,498.31707 L 63.403041,499.41083 L 63.715541,501.75459 L 66.059301,501.44208 L 69.184311,499.09832 L 70.746821,499.72333 L 71.684321,500.34833 L 73.246831,500.34833 L 75.434341,499.09832 L 76.215601,494.87955 C 76.215601,494.87955 76.528101,492.06704 77.153101,491.59829 C 77.778101,491.12954 78.090601,490.66079 78.090601,490.66079 L 76.996851,488.78578 L 74.496841,489.56703 L 71.371821,490.34828 L 69.496811,489.87953 L 66.059301,488.16077 L 61.215531,488.00452 L 57.778011,484.41076 L 58.246761,480.66074 L 58.871771,478.31698 L 56.840511,476.59822 L 54.965499,473.00445 L 55.434251,472.2232 L 61.996781,471.75445 L 64.028041,471.75445 L 64.965541,472.69195 L 65.590551,472.69195 L 65.434301,471.12944 L 69.184311,470.50444 L 71.684321,470.81694 L 73.090581,471.9107 L 71.684321,473.94196 L 71.215571,475.34821 L 73.871841,476.91072 L 78.715611,478.62948 L 80.434371,477.69198 L 78.246861,473.47321 L 77.309351,470.34819 L 78.246861,469.56694 L 74.965591,467.69193 L 74.496841,466.59817 L 74.965591,465.03567 L 74.184341,461.28565 L 71.371821,456.75438 L 69.028061,452.69186 L 71.840581,450.81685 L 74.965591,450.81685 L 76.684351,451.44185 L 80.746871,451.2856 L 84.340631,447.84809 L 85.434391,444.87932 L 89.028161,442.53556 L 90.590661,443.47307 L 93.246921,442.84806 L 96.840691,440.8168 L 97.934451,440.66055 L 98.871951,441.44181 L 103.24697,441.28556 L 105.90323,438.31679 L 106.99699,438.31679 L 110.4345,440.66055 L 112.30951,442.69181 L 111.84076,443.78557 L 112.46576,444.87932 L 114.02827,443.31682 L 117.77829,443.62932 L 118.09079,447.22308 L 119.9658,448.62934 L 126.84083,449.25434 L 132.93461,453.31686 L 134.34086,452.37936 L 139.34089,454.87937 L 141.37215,454.25437 L 143.24716,453.47311 L 147.93468,455.34812 L 152.15345,458.16063" id="AK_Pacific" style="fill:none;fill-opacity:0.26666697;stroke:#80b0f0;stroke-width:1pt" />
+ <ns0:g id="Frames" transform="translate(-18.307669,-131.99439)">
+ <ns0:path d="M 229.21212,631.12334 L 229.68087,688.43625 L 264.68114,723.74902 M 18.429285,562.99783 L 161.86786,563.31033 L 229.52462,631.59209 L 314.68151,631.74834 L 370.43191,685.18624 L 370.11941,723.43652" id="Inset_border" style="fill:none;fill-opacity:0.75;stroke:#000000;stroke-width:1.875;stroke-dasharray:none" ns1:nodetypes="ccccccccc" />
+ <ns0:rect height="590.28674" id="Outer_border" style="fill:none;stroke:#000000;stroke-width:2.5;stroke-dasharray:none" width="955.48639" x="19.444839" y="133.89751" />
+ </ns0:g>
+ <ns0:path d="M 822.91849,258.28198 A 4.1274123,3.62712 0 1 1 814.66366,258.28198 A 4.1274123,3.62712 0 1 1 822.91849,258.28198 z" id="DC" style="opacity:1;fill:#a02c2c;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" transform="matrix(0.9707988,0,0,1.0018987,24.345102,-0.4278704)" ns1:cx="818.79108" ns1:cy="258.28198" ns1:rx="4.1274123" ns1:ry="3.62712" ns1:type="arc" ns2:label="#DC" />
+</ns0:svg> \ No newline at end of file
diff --git a/tests/phpunit/data/media/Wikimedia-logo.svg b/tests/phpunit/data/media/Wikimedia-logo.svg
new file mode 100644
index 00000000..1e17acbe
--- /dev/null
+++ b/tests/phpunit/data/media/Wikimedia-logo.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Wikimedia logo" viewBox="-599 -599 1198 1198" width="1024" height="1024">
+<defs>
+ <clipPath id="mask">
+ <path d="M 47.5,-87.5 v 425 h -95 v -425 l -552,-552 v 1250 h 1199 v -1250 z"/>
+ </clipPath>
+</defs>
+<g clip-path="url(#mask)">
+ <circle id="green parts" fill="#396" r="336.5"/>
+ <circle id="blue arc" fill="none" stroke="#069" r="480.25" stroke-width="135.5"/>
+</g>
+<circle fill="#900" cy="-379.5" r="184.5" id="red circle"/>
+</svg> \ No newline at end of file
diff --git a/tests/phpunit/data/media/Xmp-exif-multilingual_test.jpg b/tests/phpunit/data/media/Xmp-exif-multilingual_test.jpg
new file mode 100644
index 00000000..f7b23025
--- /dev/null
+++ b/tests/phpunit/data/media/Xmp-exif-multilingual_test.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/animated-xmp.gif b/tests/phpunit/data/media/animated-xmp.gif
new file mode 100644
index 00000000..fcba079d
--- /dev/null
+++ b/tests/phpunit/data/media/animated-xmp.gif
Binary files differ
diff --git a/tests/phpunit/data/media/animated.gif b/tests/phpunit/data/media/animated.gif
new file mode 100644
index 00000000..a8f248b3
--- /dev/null
+++ b/tests/phpunit/data/media/animated.gif
Binary files differ
diff --git a/tests/phpunit/data/media/broken_exif_date.jpg b/tests/phpunit/data/media/broken_exif_date.jpg
new file mode 100644
index 00000000..82f62f57
--- /dev/null
+++ b/tests/phpunit/data/media/broken_exif_date.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/exif-gps.jpg b/tests/phpunit/data/media/exif-gps.jpg
new file mode 100644
index 00000000..f99b484d
--- /dev/null
+++ b/tests/phpunit/data/media/exif-gps.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/exif-user-comment.jpg b/tests/phpunit/data/media/exif-user-comment.jpg
new file mode 100644
index 00000000..9f23966a
--- /dev/null
+++ b/tests/phpunit/data/media/exif-user-comment.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/greyscale-na-png.png b/tests/phpunit/data/media/greyscale-na-png.png
new file mode 100644
index 00000000..4a4b7452
--- /dev/null
+++ b/tests/phpunit/data/media/greyscale-na-png.png
Binary files differ
diff --git a/tests/phpunit/data/media/greyscale-png.png b/tests/phpunit/data/media/greyscale-png.png
new file mode 100644
index 00000000..340a67b4
--- /dev/null
+++ b/tests/phpunit/data/media/greyscale-png.png
Binary files differ
diff --git a/tests/phpunit/data/media/iptc-timetest-invalid.jpg b/tests/phpunit/data/media/iptc-timetest-invalid.jpg
new file mode 100644
index 00000000..b03e192a
--- /dev/null
+++ b/tests/phpunit/data/media/iptc-timetest-invalid.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/iptc-timetest.jpg b/tests/phpunit/data/media/iptc-timetest.jpg
new file mode 100644
index 00000000..db9932ba
--- /dev/null
+++ b/tests/phpunit/data/media/iptc-timetest.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/jpeg-comment-binary.jpg b/tests/phpunit/data/media/jpeg-comment-binary.jpg
new file mode 100644
index 00000000..b467fe43
--- /dev/null
+++ b/tests/phpunit/data/media/jpeg-comment-binary.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/jpeg-comment-iso8859-1.jpg b/tests/phpunit/data/media/jpeg-comment-iso8859-1.jpg
new file mode 100644
index 00000000..d9ffbac1
--- /dev/null
+++ b/tests/phpunit/data/media/jpeg-comment-iso8859-1.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/jpeg-comment-multiple.jpg b/tests/phpunit/data/media/jpeg-comment-multiple.jpg
new file mode 100644
index 00000000..363c7385
--- /dev/null
+++ b/tests/phpunit/data/media/jpeg-comment-multiple.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/jpeg-comment-utf.jpg b/tests/phpunit/data/media/jpeg-comment-utf.jpg
new file mode 100644
index 00000000..d6d35b4b
--- /dev/null
+++ b/tests/phpunit/data/media/jpeg-comment-utf.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/jpeg-iptc-bad-hash.jpg b/tests/phpunit/data/media/jpeg-iptc-bad-hash.jpg
new file mode 100644
index 00000000..6464c5b8
--- /dev/null
+++ b/tests/phpunit/data/media/jpeg-iptc-bad-hash.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/jpeg-iptc-good-hash.jpg b/tests/phpunit/data/media/jpeg-iptc-good-hash.jpg
new file mode 100644
index 00000000..ef970854
--- /dev/null
+++ b/tests/phpunit/data/media/jpeg-iptc-good-hash.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/jpeg-padding-even.jpg b/tests/phpunit/data/media/jpeg-padding-even.jpg
new file mode 100644
index 00000000..c83c66bd
--- /dev/null
+++ b/tests/phpunit/data/media/jpeg-padding-even.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/jpeg-padding-odd.jpg b/tests/phpunit/data/media/jpeg-padding-odd.jpg
new file mode 100644
index 00000000..25b93308
--- /dev/null
+++ b/tests/phpunit/data/media/jpeg-padding-odd.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/jpeg-xmp-alt.jpg b/tests/phpunit/data/media/jpeg-xmp-alt.jpg
new file mode 100644
index 00000000..0e2c3f63
--- /dev/null
+++ b/tests/phpunit/data/media/jpeg-xmp-alt.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/jpeg-xmp-psir.jpg b/tests/phpunit/data/media/jpeg-xmp-psir.jpg
new file mode 100644
index 00000000..4d19fcbe
--- /dev/null
+++ b/tests/phpunit/data/media/jpeg-xmp-psir.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/jpeg-xmp-psir.xmp b/tests/phpunit/data/media/jpeg-xmp-psir.xmp
new file mode 100644
index 00000000..fee6ee18
--- /dev/null
+++ b/tests/phpunit/data/media/jpeg-xmp-psir.xmp
@@ -0,0 +1,35 @@
+<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>
+<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 7.30'>
+<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
+
+ <rdf:Description rdf:about=''
+ xmlns:dc='http://purl.org/dc/elements/1.1/'>
+ <dc:identifier>jpeg-xmp-psir.jpg</dc:identifier>
+ </rdf:Description>
+</rdf:RDF>
+</x:xmpmeta>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<?xpacket end='w'?> \ No newline at end of file
diff --git a/tests/phpunit/data/media/landscape-plain.jpg b/tests/phpunit/data/media/landscape-plain.jpg
new file mode 100644
index 00000000..cf296555
--- /dev/null
+++ b/tests/phpunit/data/media/landscape-plain.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/nonanimated.gif b/tests/phpunit/data/media/nonanimated.gif
new file mode 100644
index 00000000..9e52a7f0
--- /dev/null
+++ b/tests/phpunit/data/media/nonanimated.gif
Binary files differ
diff --git a/tests/phpunit/data/media/portrait-rotated.jpg b/tests/phpunit/data/media/portrait-rotated.jpg
new file mode 100644
index 00000000..445feaed
--- /dev/null
+++ b/tests/phpunit/data/media/portrait-rotated.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/rgb-na-png.png b/tests/phpunit/data/media/rgb-na-png.png
new file mode 100644
index 00000000..2f2a5ca0
--- /dev/null
+++ b/tests/phpunit/data/media/rgb-na-png.png
Binary files differ
diff --git a/tests/phpunit/data/media/rgb-png.png b/tests/phpunit/data/media/rgb-png.png
new file mode 100644
index 00000000..6f40cc92
--- /dev/null
+++ b/tests/phpunit/data/media/rgb-png.png
Binary files differ
diff --git a/tests/phpunit/data/media/test.jpg b/tests/phpunit/data/media/test.jpg
new file mode 100644
index 00000000..cb084253
--- /dev/null
+++ b/tests/phpunit/data/media/test.jpg
Binary files differ
diff --git a/tests/phpunit/data/media/test.tiff b/tests/phpunit/data/media/test.tiff
new file mode 100644
index 00000000..6a36f760
--- /dev/null
+++ b/tests/phpunit/data/media/test.tiff
Binary files differ
diff --git a/tests/phpunit/data/media/xmp.png b/tests/phpunit/data/media/xmp.png
new file mode 100644
index 00000000..6b9f7a87
--- /dev/null
+++ b/tests/phpunit/data/media/xmp.png
Binary files differ
diff --git a/tests/phpunit/data/xmp/1.result.php b/tests/phpunit/data/xmp/1.result.php
new file mode 100644
index 00000000..beead1bd
--- /dev/null
+++ b/tests/phpunit/data/xmp/1.result.php
@@ -0,0 +1,8 @@
+<?php
+
+$result = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ 'Flash' => '9'
+ )
+);
diff --git a/tests/phpunit/data/xmp/1.xmp b/tests/phpunit/data/xmp/1.xmp
new file mode 100644
index 00000000..66e15427
--- /dev/null
+++ b/tests/phpunit/data/xmp/1.xmp
@@ -0,0 +1,11 @@
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core
+ 4.1.3-c001 49.282696, Mon Apr 02 2007 21:16:10 ">
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/"
+ exif:DigitalZoomRatio="0/10">
+<exif:Flash rdf:parseType='Resource'>
+<exif:Fired>True</exif:Fired> <exif:Return>0</exif:Return> <exif:Mode>1</exif:Mode> <exif:Function>False</exif:Function> <exif:RedEyeMode>False</exif:RedEyeMode></exif:Flash> </rdf:Description> </rdf:RDF> </x:xmpmeta>
+
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/xmp/2.result.php b/tests/phpunit/data/xmp/2.result.php
new file mode 100644
index 00000000..beead1bd
--- /dev/null
+++ b/tests/phpunit/data/xmp/2.result.php
@@ -0,0 +1,8 @@
+<?php
+
+$result = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ 'Flash' => '9'
+ )
+);
diff --git a/tests/phpunit/data/xmp/2.xmp b/tests/phpunit/data/xmp/2.xmp
new file mode 100644
index 00000000..0fa6a894
--- /dev/null
+++ b/tests/phpunit/data/xmp/2.xmp
@@ -0,0 +1,12 @@
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core
+ 4.1.3-c001 49.282696, Mon Apr 02 2007 21:16:10 ">
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/"
+ exif:DigitalZoomRatio="0/10">
+<exif:Flash>
+<rdf:Description exif:Return="0">
+<exif:Fired>True</exif:Fired> <exif:Mode>1</exif:Mode> <exif:Function>False</exif:Function> <exif:RedEyeMode>False</exif:RedEyeMode></rdf:Description></exif:Flash> </rdf:Description> </rdf:RDF> </x:xmpmeta>
+
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/xmp/3-invalid.result.php b/tests/phpunit/data/xmp/3-invalid.result.php
new file mode 100644
index 00000000..5741b2c9
--- /dev/null
+++ b/tests/phpunit/data/xmp/3-invalid.result.php
@@ -0,0 +1,7 @@
+<?php
+
+$result = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ )
+);
diff --git a/tests/phpunit/data/xmp/3-invalid.xmp b/tests/phpunit/data/xmp/3-invalid.xmp
new file mode 100644
index 00000000..2425e254
--- /dev/null
+++ b/tests/phpunit/data/xmp/3-invalid.xmp
@@ -0,0 +1,31 @@
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core
+ 4.1.3-c001 49.282696, Mon Apr 02 2007 21:16:10 ">
+<!--
+This file has an invalid flash compoenent (one of the values are a qualifier)
+-->
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/"
+>
+<exif:DigitalZoomRatio>
+
+<rdf:Description>
+<rdf:value>
+0/10
+</rdf:value>
+<exif:foobarbaz>fred</exif:foobarbaz>
+
+</rdf:Description>
+
+</exif:DigitalZoomRatio>
+
+<exif:Flash>
+<rdf:Description exif:Return="0">
+<exif:Mode><rdf:Description>
+<rdf:value>1</rdf:value>
+<exif:Fired>False</exif:Fired> <!-- qualifier. should be ignored-->
+</rdf:Description>
+</exif:Mode> <exif:Function>False</exif:Function> <exif:RedEyeMode>False</exif:RedEyeMode></rdf:Description></exif:Flash> </rdf:Description> </rdf:RDF> </x:xmpmeta>
+
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/xmp/3.result.php b/tests/phpunit/data/xmp/3.result.php
new file mode 100644
index 00000000..beead1bd
--- /dev/null
+++ b/tests/phpunit/data/xmp/3.result.php
@@ -0,0 +1,8 @@
+<?php
+
+$result = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ 'Flash' => '9'
+ )
+);
diff --git a/tests/phpunit/data/xmp/3.xmp b/tests/phpunit/data/xmp/3.xmp
new file mode 100644
index 00000000..2cf19883
--- /dev/null
+++ b/tests/phpunit/data/xmp/3.xmp
@@ -0,0 +1,29 @@
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core
+ 4.1.3-c001 49.282696, Mon Apr 02 2007 21:16:10 ">
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/"
+>
+<exif:DigitalZoomRatio>
+
+<rdf:Description>
+<rdf:value>
+0/10
+</rdf:value>
+<exif:foobarbaz>fred</exif:foobarbaz>
+
+</rdf:Description>
+
+</exif:DigitalZoomRatio>
+
+<exif:Flash>
+<rdf:Description exif:Return="0">
+<exif:Fired>True</exif:Fired>
+<exif:Mode><rdf:Description>
+<rdf:value>1</rdf:value>
+<exif:Fired>False</exif:Fired> <!-- qualifier. should be ignored-->
+</rdf:Description>
+</exif:Mode> <exif:Function>False</exif:Function> <exif:RedEyeMode>False</exif:RedEyeMode></rdf:Description></exif:Flash> </rdf:Description> </rdf:RDF> </x:xmpmeta>
+
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/xmp/4.result.php b/tests/phpunit/data/xmp/4.result.php
new file mode 100644
index 00000000..5741b2c9
--- /dev/null
+++ b/tests/phpunit/data/xmp/4.result.php
@@ -0,0 +1,7 @@
+<?php
+
+$result = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ )
+);
diff --git a/tests/phpunit/data/xmp/4.xmp b/tests/phpunit/data/xmp/4.xmp
new file mode 100644
index 00000000..29eb614b
--- /dev/null
+++ b/tests/phpunit/data/xmp/4.xmp
@@ -0,0 +1,22 @@
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core
+ 4.1.3-c001 49.282696, Mon Apr 02 2007 21:16:10 ">
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<!-- Valid output is just the DigitalZoomRatio
+as the flash is a qualifier
+-->
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/">
+ <exif:DigitalZoomRatio>
+<rdf:Description>
+<rdf:value>
+0/10
+</rdf:value>
+<exif:Flash rdf:parseType='Resource'>
+<exif:Fired>True</exif:Fired> <exif:Return>0</exif:Return> <exif:Mode>1</exif:Mode> <exif:Function>False</exif:Function> <exif:RedEyeMode>False</exif:RedEyeMode></exif:Flash>
+</rdf:Description>
+</exif:DigitalZoomRatio>
+</rdf:Description> </rdf:RDF> </x:xmpmeta>
+
+
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/xmp/5.result.php b/tests/phpunit/data/xmp/5.result.php
new file mode 100644
index 00000000..5741b2c9
--- /dev/null
+++ b/tests/phpunit/data/xmp/5.result.php
@@ -0,0 +1,7 @@
+<?php
+
+$result = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ )
+);
diff --git a/tests/phpunit/data/xmp/5.xmp b/tests/phpunit/data/xmp/5.xmp
new file mode 100644
index 00000000..3cc61d68
--- /dev/null
+++ b/tests/phpunit/data/xmp/5.xmp
@@ -0,0 +1,16 @@
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core
+ 4.1.3-c001 49.282696, Mon Apr 02 2007 21:16:10 ">
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/">
+ <exif:DigitalZoomRatio>
+<rdf:Description rdf:value="0/10">
+<exif:Flash rdf:parseType='Resource'>
+<exif:Fired>True</exif:Fired> <exif:Return>0</exif:Return> <exif:Mode>1</exif:Mode> <exif:Function>False</exif:Function> <exif:RedEyeMode>False</exif:RedEyeMode></exif:Flash>
+</rdf:Description>
+</exif:DigitalZoomRatio>
+</rdf:Description> </rdf:RDF> </x:xmpmeta>
+
+
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/xmp/6.result.php b/tests/phpunit/data/xmp/6.result.php
new file mode 100644
index 00000000..beead1bd
--- /dev/null
+++ b/tests/phpunit/data/xmp/6.result.php
@@ -0,0 +1,8 @@
+<?php
+
+$result = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ 'Flash' => '9'
+ )
+);
diff --git a/tests/phpunit/data/xmp/6.xmp b/tests/phpunit/data/xmp/6.xmp
new file mode 100644
index 00000000..f435ab23
--- /dev/null
+++ b/tests/phpunit/data/xmp/6.xmp
@@ -0,0 +1,18 @@
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core
+ 4.1.3-c001 49.282696, Mon Apr 02 2007 21:16:10 ">
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/">
+<exif:DigitalZoomRatio>
+0/10
+</exif:DigitalZoomRatio>
+</rdf:Description>
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/">
+
+<exif:Flash rdf:parseType='Resource'>
+<exif:Fired>True</exif:Fired> <exif:Return>0</exif:Return> <exif:Mode>1</exif:Mode> <exif:Function>False</exif:Function> <exif:RedEyeMode>False</exif:RedEyeMode></exif:Flash> </rdf:Description> </rdf:RDF> </x:xmpmeta>
+
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/xmp/7.result.php b/tests/phpunit/data/xmp/7.result.php
new file mode 100644
index 00000000..0efcfa36
--- /dev/null
+++ b/tests/phpunit/data/xmp/7.result.php
@@ -0,0 +1,52 @@
+<?php
+$result = array (
+ 'xmp-exif' =>
+ array (
+ 'CameraOwnerName' => 'Me!',
+ ),
+ 'xmp-general' =>
+ array (
+ 'LicenseUrl' => 'http://creativecommons.com/cc-by-2.9',
+ 'ImageDescription' =>
+ array (
+ 'x-default' => 'Test image for the cc: xmp: xmpRights: namespaces in xmp',
+ '_type' => 'lang',
+ ),
+ 'ObjectName' =>
+ array (
+ 'x-default' => 'xmp core/xmp rights/cc ns test',
+ '_type' => 'lang',
+ ),
+ 'DateTimeDigitized' => '2005:04:03',
+ 'Software' => 'The one true editor: Vi (ok i used gimp)',
+ 'Identifier' =>
+ array (
+ 0 => 'http://example.com/identifierurl',
+ 1 => 'urn:sha1:342524abcdef',
+ '_type' => 'ul',
+ ),
+ 'Label' => 'Test image',
+ 'DateTimeMetadata' => '2011:05:12',
+ 'DateTime' => '2007:03:04 06:34:10',
+ 'Nickname' => 'My little xmp test image',
+ 'Rating' => '5',
+ 'RightsCertificate' => 'http://example.com/rights-certificate/',
+ 'Copyrighted' => 'True',
+ 'CopyrightOwner' =>
+ array (
+ 0 => 'Bawolff is copyright owner',
+ '_type' => 'ul',
+ ),
+ 'UsageTerms' =>
+ array (
+ 'x-default' => 'do whatever you want',
+ 'en-gb' => 'Do whatever you want in british english',
+ '_type' => 'lang',
+ ),
+ 'WebStatement' => 'http://example.com/web_statement',
+ ),
+ 'xmp-deprecated' =>
+ array (
+ 'Identifier' => 'http://example.com/identifierurl/wrong',
+ ),
+);
diff --git a/tests/phpunit/data/xmp/7.xmp b/tests/phpunit/data/xmp/7.xmp
new file mode 100644
index 00000000..e18e13d9
--- /dev/null
+++ b/tests/phpunit/data/xmp/7.xmp
@@ -0,0 +1,67 @@
+<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>
+<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 7.30'>
+<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
+
+ <rdf:Description rdf:about=''
+ xmlns:aux='http://ns.adobe.com/exif/1.0/aux/'>
+ <aux:OwnerName>Me!</aux:OwnerName>
+ </rdf:Description>
+
+ <rdf:Description rdf:about=''
+ xmlns:cc='http://creativecommons.org/ns#'>
+ <cc:license>http://creativecommons.com/cc-by-2.9</cc:license>
+ </rdf:Description>
+
+ <rdf:Description rdf:about=''
+ xmlns:dc='http://purl.org/dc/elements/1.1/'>
+ <dc:description>
+ <rdf:Alt>
+ <rdf:li xml:lang='x-default'>Test image for the cc: xmp: xmpRights: namespaces in xmp</rdf:li>
+ </rdf:Alt>
+ </dc:description>
+ <dc:identifier>http://example.com/identifierurl/wrong</dc:identifier>
+ <dc:title>
+ <rdf:Alt>
+ <rdf:li xml:lang='x-default'>xmp core/xmp rights/cc ns test</rdf:li>
+ </rdf:Alt>
+ </dc:title>
+ </rdf:Description>
+
+ <rdf:Description rdf:about=''
+ xmlns:xmp='http://ns.adobe.com/xap/1.0/'>
+ <xmp:CreateDate>2005-04-03</xmp:CreateDate>
+ <xmp:CreatorTool>The one true editor: Vi (ok i used gimp)</xmp:CreatorTool>
+ <xmp:Identifier>
+ <rdf:Bag>
+ <rdf:li>http://example.com/identifierurl
+</rdf:li>
+ <rdf:li>urn:sha1:342524abcdef</rdf:li>
+ </rdf:Bag>
+ </xmp:Identifier>
+ <xmp:Label>Test image</xmp:Label>
+ <xmp:MetadataDate>2011-05-12</xmp:MetadataDate>
+ <xmp:ModifyDate>2007-03-04T12:34:10-06:00</xmp:ModifyDate>
+ <xmp:Nickname>My little xmp test image</xmp:Nickname>
+ <xmp:Rating>7</xmp:Rating>
+ </rdf:Description>
+
+ <rdf:Description rdf:about=''
+ xmlns:xmpRights='http://ns.adobe.com/xap/1.0/rights/'>
+ <xmpRights:Certificate>http://example.com/rights-certificate/</xmpRights:Certificate>
+ <xmpRights:Marked>True</xmpRights:Marked>
+ <xmpRights:Owner>
+ <rdf:Bag>
+ <rdf:li>Bawolff is copyright owner</rdf:li>
+ </rdf:Bag>
+ </xmpRights:Owner>
+ <xmpRights:UsageTerms>
+ <rdf:Alt>
+ <rdf:li xml:lang='x-default'>do whatever you want</rdf:li>
+ <rdf:li xml:lang='en-GB'>Do whatever you want in british english</rdf:li>
+ </rdf:Alt>
+ </xmpRights:UsageTerms>
+ <xmpRights:WebStatement>http://example.com/web_statement</xmpRights:WebStatement>
+ </rdf:Description>
+</rdf:RDF>
+</x:xmpmeta>
+<?xpacket end='r'?>
diff --git a/tests/phpunit/data/xmp/README b/tests/phpunit/data/xmp/README
new file mode 100644
index 00000000..bd949176
--- /dev/null
+++ b/tests/phpunit/data/xmp/README
@@ -0,0 +1,3 @@
+This directory contains a bunch of XMP files
+as well as a bunch of php files containing what the
+parsed version of the XMP looks like.
diff --git a/tests/phpunit/data/xmp/bag-for-seq.result.php b/tests/phpunit/data/xmp/bag-for-seq.result.php
new file mode 100644
index 00000000..b5244f88
--- /dev/null
+++ b/tests/phpunit/data/xmp/bag-for-seq.result.php
@@ -0,0 +1,10 @@
+<?php
+
+$result = array(
+ 'xmp-general' => array(
+ 'Artist' => array(
+ '_type' => 'ul',
+ 0 => 'The author',
+ )
+ )
+);
diff --git a/tests/phpunit/data/xmp/bag-for-seq.xmp b/tests/phpunit/data/xmp/bag-for-seq.xmp
new file mode 100644
index 00000000..c6ed5b7c
--- /dev/null
+++ b/tests/phpunit/data/xmp/bag-for-seq.xmp
@@ -0,0 +1 @@
+<?xpacket begin=""?> <x:xmpmeta xmlns:x="adobe:ns:meta/"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"> <dc:creator> <rdf:Bag> <rdf:li>The author</rdf:li> </rdf:Bag> </dc:creator> </rdf:Description> </rdf:RDF> </x:xmpmeta>
diff --git a/tests/phpunit/data/xmp/flash.result.php b/tests/phpunit/data/xmp/flash.result.php
new file mode 100644
index 00000000..018c0ac1
--- /dev/null
+++ b/tests/phpunit/data/xmp/flash.result.php
@@ -0,0 +1,8 @@
+<?php
+
+$result = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ 'Flash' => '127'
+ )
+);
diff --git a/tests/phpunit/data/xmp/flash.xmp b/tests/phpunit/data/xmp/flash.xmp
new file mode 100644
index 00000000..b1373cc2
--- /dev/null
+++ b/tests/phpunit/data/xmp/flash.xmp
@@ -0,0 +1,11 @@
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core
+ 4.1.3-c001 49.282696, Mon Apr 02 2007 21:16:10 ">
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/"
+ exif:DigitalZoomRatio="0/10">
+<exif:Flash rdf:parseType='Resource'>
+<exif:Fired>True</exif:Fired> <exif:Return>3</exif:Return> <exif:Mode>3</exif:Mode> <exif:Function>True</exif:Function> <exif:RedEyeMode>True</exif:RedEyeMode></exif:Flash> </rdf:Description> </rdf:RDF> </x:xmpmeta>
+
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/xmp/invalid-child-not-struct.result.php b/tests/phpunit/data/xmp/invalid-child-not-struct.result.php
new file mode 100644
index 00000000..5741b2c9
--- /dev/null
+++ b/tests/phpunit/data/xmp/invalid-child-not-struct.result.php
@@ -0,0 +1,7 @@
+<?php
+
+$result = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ )
+);
diff --git a/tests/phpunit/data/xmp/invalid-child-not-struct.xmp b/tests/phpunit/data/xmp/invalid-child-not-struct.xmp
new file mode 100644
index 00000000..6aa0c10b
--- /dev/null
+++ b/tests/phpunit/data/xmp/invalid-child-not-struct.xmp
@@ -0,0 +1,12 @@
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core
+ 4.1.3-c001 49.282696, Mon Apr 02 2007 21:16:10 ">
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/"
+ exif:DigitalZoomRatio="0/10">
+<exif:Fired>True</exif:Fired> <exif:Return>0</exif:Return> <exif:Mode>1</exif:Mode> <exif:Function>False</exif:Function> <exif:RedEyeMode>False</exif:RedEyeMode>
+
+ </rdf:Description> </rdf:RDF> </x:xmpmeta>
+
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/xmp/no-namespace.result.php b/tests/phpunit/data/xmp/no-namespace.result.php
new file mode 100644
index 00000000..3ff69201
--- /dev/null
+++ b/tests/phpunit/data/xmp/no-namespace.result.php
@@ -0,0 +1,7 @@
+<?php
+
+$result = array( 'xmp-exif' =>
+ array(
+ 'FNumber' => '28/10',
+ )
+);
diff --git a/tests/phpunit/data/xmp/no-namespace.xmp b/tests/phpunit/data/xmp/no-namespace.xmp
new file mode 100644
index 00000000..7d6cdb2f
--- /dev/null
+++ b/tests/phpunit/data/xmp/no-namespace.xmp
@@ -0,0 +1,11 @@
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<!-- Testing it handles random non-namespaced properties in files ok.
+ Some older photoshop's did not include the rdf: prefix on about. -->
+<rdf:Description
+ about=""
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/"
+ exif:FNumber="28/10">
+</rdf:Description>
+</rdf:RDF>
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/xmp/no-recognized-props.result.php b/tests/phpunit/data/xmp/no-recognized-props.result.php
new file mode 100644
index 00000000..b3ca9f5a
--- /dev/null
+++ b/tests/phpunit/data/xmp/no-recognized-props.result.php
@@ -0,0 +1,2 @@
+<?php
+$result = array();
diff --git a/tests/phpunit/data/xmp/no-recognized-props.xmp b/tests/phpunit/data/xmp/no-recognized-props.xmp
new file mode 100644
index 00000000..54e80901
--- /dev/null
+++ b/tests/phpunit/data/xmp/no-recognized-props.xmp
@@ -0,0 +1,8 @@
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/not-exif-namespace"
+ exif:FNumber="2/10">
+</rdf:Description>
+</rdf:RDF>
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/xmp/utf16BE.result.php b/tests/phpunit/data/xmp/utf16BE.result.php
new file mode 100644
index 00000000..ac7ea506
--- /dev/null
+++ b/tests/phpunit/data/xmp/utf16BE.result.php
@@ -0,0 +1,12 @@
+<?php
+
+$result = array(
+ 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ ),
+ 'xmp-general' =>
+ array(
+ 'Label' => 'ôŠ¯'
+ ),
+);
diff --git a/tests/phpunit/data/xmp/utf16BE.xmp b/tests/phpunit/data/xmp/utf16BE.xmp
new file mode 100644
index 00000000..0cf60d60
--- /dev/null
+++ b/tests/phpunit/data/xmp/utf16BE.xmp
Binary files differ
diff --git a/tests/phpunit/data/xmp/utf16LE.result.php b/tests/phpunit/data/xmp/utf16LE.result.php
new file mode 100644
index 00000000..ac7ea506
--- /dev/null
+++ b/tests/phpunit/data/xmp/utf16LE.result.php
@@ -0,0 +1,12 @@
+<?php
+
+$result = array(
+ 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ ),
+ 'xmp-general' =>
+ array(
+ 'Label' => 'ôŠ¯'
+ ),
+);
diff --git a/tests/phpunit/data/xmp/utf16LE.xmp b/tests/phpunit/data/xmp/utf16LE.xmp
new file mode 100644
index 00000000..66d71f4c
--- /dev/null
+++ b/tests/phpunit/data/xmp/utf16LE.xmp
Binary files differ
diff --git a/tests/phpunit/data/xmp/utf32BE.result.php b/tests/phpunit/data/xmp/utf32BE.result.php
new file mode 100644
index 00000000..ac7ea506
--- /dev/null
+++ b/tests/phpunit/data/xmp/utf32BE.result.php
@@ -0,0 +1,12 @@
+<?php
+
+$result = array(
+ 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ ),
+ 'xmp-general' =>
+ array(
+ 'Label' => 'ôŠ¯'
+ ),
+);
diff --git a/tests/phpunit/data/xmp/utf32BE.xmp b/tests/phpunit/data/xmp/utf32BE.xmp
new file mode 100644
index 00000000..06afdf92
--- /dev/null
+++ b/tests/phpunit/data/xmp/utf32BE.xmp
Binary files differ
diff --git a/tests/phpunit/data/xmp/utf32LE.result.php b/tests/phpunit/data/xmp/utf32LE.result.php
new file mode 100644
index 00000000..ac7ea506
--- /dev/null
+++ b/tests/phpunit/data/xmp/utf32LE.result.php
@@ -0,0 +1,12 @@
+<?php
+
+$result = array(
+ 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ ),
+ 'xmp-general' =>
+ array(
+ 'Label' => 'ôŠ¯'
+ ),
+);
diff --git a/tests/phpunit/data/xmp/utf32LE.xmp b/tests/phpunit/data/xmp/utf32LE.xmp
new file mode 100644
index 00000000..bf2097fe
--- /dev/null
+++ b/tests/phpunit/data/xmp/utf32LE.xmp
Binary files differ
diff --git a/tests/phpunit/data/xmp/xmpExt.result.php b/tests/phpunit/data/xmp/xmpExt.result.php
new file mode 100644
index 00000000..beead1bd
--- /dev/null
+++ b/tests/phpunit/data/xmp/xmpExt.result.php
@@ -0,0 +1,8 @@
+<?php
+
+$result = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ 'Flash' => '9'
+ )
+);
diff --git a/tests/phpunit/data/xmp/xmpExt.xmp b/tests/phpunit/data/xmp/xmpExt.xmp
new file mode 100644
index 00000000..da0383f8
--- /dev/null
+++ b/tests/phpunit/data/xmp/xmpExt.xmp
@@ -0,0 +1,13 @@
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core
+ 4.1.3-c001 49.282696, Mon Apr 02 2007 21:16:10 ">
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/"
+ xmlns:xmpNote="http://ns.adobe.com/xmp/note/"
+ exif:DigitalZoomRatio="0/10"
+ xmpNote:HasExtendedXMP="28C74E0AC2D796886759006FBE2E57B7">
+<exif:Flash rdf:parseType='Resource'>
+<exif:Fired>True</exif:Fired> <exif:Return>0</exif:Return> <exif:Mode>1</exif:Mode> <exif:Function>False</exif:Function> <exif:RedEyeMode>False</exif:RedEyeMode></exif:Flash> </rdf:Description> </rdf:RDF> </x:xmpmeta>
+
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/xmp/xmpExt2.xmp b/tests/phpunit/data/xmp/xmpExt2.xmp
new file mode 100644
index 00000000..060abb2c
--- /dev/null
+++ b/tests/phpunit/data/xmp/xmpExt2.xmp
@@ -0,0 +1,8 @@
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description
+ rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/"
+ exif:FNumber="2/10">
+</rdf:Description>
+</rdf:RDF>
+<?xpacket end="w"?>
diff --git a/tests/phpunit/data/zip/cd-gap.zip b/tests/phpunit/data/zip/cd-gap.zip
new file mode 100644
index 00000000..b5ae6ccd
--- /dev/null
+++ b/tests/phpunit/data/zip/cd-gap.zip
Binary files differ
diff --git a/tests/phpunit/data/zip/cd-truncated.zip b/tests/phpunit/data/zip/cd-truncated.zip
new file mode 100644
index 00000000..4d40d7d4
--- /dev/null
+++ b/tests/phpunit/data/zip/cd-truncated.zip
Binary files differ
diff --git a/tests/phpunit/data/zip/class-trailing-null.zip b/tests/phpunit/data/zip/class-trailing-null.zip
new file mode 100644
index 00000000..31dcf3d8
--- /dev/null
+++ b/tests/phpunit/data/zip/class-trailing-null.zip
Binary files differ
diff --git a/tests/phpunit/data/zip/class-trailing-slash.zip b/tests/phpunit/data/zip/class-trailing-slash.zip
new file mode 100644
index 00000000..9eb1f037
--- /dev/null
+++ b/tests/phpunit/data/zip/class-trailing-slash.zip
Binary files differ
diff --git a/tests/phpunit/data/zip/class.zip b/tests/phpunit/data/zip/class.zip
new file mode 100644
index 00000000..98a625b7
--- /dev/null
+++ b/tests/phpunit/data/zip/class.zip
Binary files differ
diff --git a/tests/phpunit/data/zip/empty.zip b/tests/phpunit/data/zip/empty.zip
new file mode 100644
index 00000000..15cb0ecb
--- /dev/null
+++ b/tests/phpunit/data/zip/empty.zip
Binary files differ
diff --git a/tests/phpunit/data/zip/looks-like-zip64.zip b/tests/phpunit/data/zip/looks-like-zip64.zip
new file mode 100644
index 00000000..7428cddd
--- /dev/null
+++ b/tests/phpunit/data/zip/looks-like-zip64.zip
Binary files differ
diff --git a/tests/phpunit/data/zip/nosig.zip b/tests/phpunit/data/zip/nosig.zip
new file mode 100644
index 00000000..a22c73a4
--- /dev/null
+++ b/tests/phpunit/data/zip/nosig.zip
Binary files differ
diff --git a/tests/phpunit/data/zip/split.zip b/tests/phpunit/data/zip/split.zip
new file mode 100644
index 00000000..6984ae6d
--- /dev/null
+++ b/tests/phpunit/data/zip/split.zip
Binary files differ
diff --git a/tests/phpunit/data/zip/trail.zip b/tests/phpunit/data/zip/trail.zip
new file mode 100644
index 00000000..50bcea12
--- /dev/null
+++ b/tests/phpunit/data/zip/trail.zip
Binary files differ
diff --git a/tests/phpunit/data/zip/wrong-cd-start-disk.zip b/tests/phpunit/data/zip/wrong-cd-start-disk.zip
new file mode 100644
index 00000000..59b45938
--- /dev/null
+++ b/tests/phpunit/data/zip/wrong-cd-start-disk.zip
Binary files differ
diff --git a/tests/phpunit/data/zip/wrong-central-entry-sig.zip b/tests/phpunit/data/zip/wrong-central-entry-sig.zip
new file mode 100644
index 00000000..05329b43
--- /dev/null
+++ b/tests/phpunit/data/zip/wrong-central-entry-sig.zip
Binary files differ
diff --git a/tests/phpunit/includes/ArticleTablesTest.php b/tests/phpunit/includes/ArticleTablesTest.php
new file mode 100644
index 00000000..01776c95
--- /dev/null
+++ b/tests/phpunit/includes/ArticleTablesTest.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @group Database
+ */
+class ArticleTablesTest extends MediaWikiLangTestCase {
+
+ function testbug14404() {
+ global $wgUser, $wgContLang, $wgLanguageCode, $wgLang;
+
+ $title = Title::newFromText("Bug 14404");
+ $article = new Article( $title );
+ $wgUser = new User();
+ $wgUser->mRights = array( 'createpage', 'edit', 'purge' );
+ $wgLanguageCode = 'es';
+ $wgContLang = Language::factory( 'es' );
+
+ $wgLang = Language::factory( 'fr' );
+ $status = $article->doEdit( '{{:{{int:history}}}}', 'Test code for bug 14404', 0 );
+ $templates1 = $article->getUsedTemplates();
+
+ $wgLang = Language::factory( 'de' );
+ $article->mParserOptions = null; // Let it pick the new user language
+ $article->mPreparedEdit = false; // In order to force the rerendering of the same wikitext
+
+ // We need an edit, a purge is not enough to regenerate the tables
+ $status = $article->doEdit( '{{:{{int:history}}}}', 'Test code for bug 14404', EDIT_UPDATE );
+ $templates2 = $article->getUsedTemplates();
+
+ $this->assertEquals( $templates1, $templates2 );
+ $this->assertEquals( $templates1[0]->getFullText(), 'Historial' );
+ }
+
+}
diff --git a/tests/phpunit/includes/ArticleTest.php b/tests/phpunit/includes/ArticleTest.php
new file mode 100644
index 00000000..285efee9
--- /dev/null
+++ b/tests/phpunit/includes/ArticleTest.php
@@ -0,0 +1,82 @@
+<?php
+
+class ArticleTest extends MediaWikiTestCase {
+
+ private $title; // holds a Title object
+ private $article; // holds an article
+
+ /** creates a title object and its article object */
+ function setUp() {
+ $this->title = Title::makeTitle( NS_MAIN, 'SomePage' );
+ $this->article = new Article( $this->title );
+
+ }
+
+ /** cleanup title object and its article object */
+ function tearDown() {
+ $this->title = null;
+ $this->article = null;
+
+ }
+
+ function testImplementsGetMagic() {
+ $this->assertEquals( -1, $this->article->mCounter, "Article __get magic" );
+ }
+
+ /**
+ * @depends testImplementsGetMagic
+ */
+ function testImplementsSetMagic() {
+
+ $this->article->mCounter = 2;
+ $this->assertEquals( 2, $this->article->mCounter, "Article __set magic" );
+ }
+
+ /**
+ * @depends testImplementsSetMagic
+ */
+ function testImplementsCallMagic() {
+ $this->article->mCounter = 33;
+ $this->assertEquals( 33, $this->article->getCount(), "Article __call magic" );
+ }
+
+ function testGetOrSetOnNewProperty() {
+ $this->article->ext_someNewProperty = 12;
+ $this->assertEquals( 12, $this->article->ext_someNewProperty,
+ "Article get/set magic on new field" );
+
+ $this->article->ext_someNewProperty = -8;
+ $this->assertEquals( -8, $this->article->ext_someNewProperty,
+ "Article get/set magic on update to new field" );
+ }
+
+ /**
+ * Checks for the existence of the backwards compatibility static functions (forwarders to WikiPage class)
+ */
+ function testStaticFunctions() {
+ $this->assertEquals( WikiPage::selectFields(), Article::selectFields(),
+ "Article static functions" );
+ $this->assertEquals( true, is_callable( "Article::onArticleCreate" ),
+ "Article static functions" );
+ $this->assertEquals( true, is_callable( "Article::onArticleDelete" ),
+ "Article static functions" );
+ $this->assertEquals( true, is_callable( "ImagePage::onArticleEdit" ),
+ "Article static functions" );
+ $this->assertTrue( is_string( CategoryPage::getAutosummary( '', '', 0 ) ),
+ "Article static functions" );
+ }
+
+ function testWikiPageFactory() {
+ $title = Title::makeTitle( NS_FILE, 'Someimage.png' );
+ $page = WikiPage::factory( $title );
+ $this->assertEquals( 'WikiFilePage', get_class( $page ) );
+
+ $title = Title::makeTitle( NS_CATEGORY, 'SomeCategory' );
+ $page = WikiPage::factory( $title );
+ $this->assertEquals( 'WikiCategoryPage', get_class( $page ) );
+
+ $title = Title::makeTitle( NS_MAIN, 'SomePage' );
+ $page = WikiPage::factory( $title );
+ $this->assertEquals( 'WikiPage', get_class( $page ) );
+ }
+}
diff --git a/tests/phpunit/includes/BlockTest.php b/tests/phpunit/includes/BlockTest.php
new file mode 100644
index 00000000..2f224ba8
--- /dev/null
+++ b/tests/phpunit/includes/BlockTest.php
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * @group Database
+ */
+class BlockTest extends MediaWikiLangTestCase {
+
+ const REASON = "Some reason";
+
+ private $block, $madeAt;
+
+ /* variable used to save up the blockID we insert in this test suite */
+ private $blockId;
+
+ function setUp() {
+ global $wgContLang;
+ parent::setUp();
+ $wgContLang = Language::factory( 'en' );
+ }
+
+ function addDBData() {
+ //$this->dumpBlocks();
+
+ $user = User::newFromName( 'UTBlockee' );
+ if( $user->getID() == 0 ) {
+ $user->addToDatabase();
+ $user->setPassword( 'UTBlockeePassword' );
+
+ $user->saveSettings();
+ }
+
+ // Delete the last round's block if it's still there
+ $oldBlock = Block::newFromTarget( 'UTBlockee' );
+ if ( $oldBlock ) {
+ // An old block will prevent our new one from saving.
+ $oldBlock->delete();
+ }
+
+ $this->block = new Block( 'UTBlockee', 1, 0,
+ self::REASON
+ );
+ $this->madeAt = wfTimestamp( TS_MW );
+
+ $this->block->insert();
+ // save up ID for use in assertion. Since ID is an autoincrement,
+ // its value might change depending on the order the tests are run.
+ // ApiBlockTest insert its own blocks!
+ $newBlockId = $this->block->getId();
+ if ($newBlockId) {
+ $this->blockId = $newBlockId;
+ } else {
+ throw new MWException( "Failed to insert block for BlockTest; old leftover block remaining?" );
+ }
+ }
+
+ /**
+ * debug function : dump the ipblocks table
+ */
+ function dumpBlocks() {
+ $v = $this->db->query( 'SELECT * FROM unittest_ipblocks' );
+ print "Got " . $v->numRows() . " rows. Full dump follow:\n";
+ foreach( $v as $row ) {
+ print_r( $row );
+ }
+ }
+
+ function testInitializerFunctionsReturnCorrectBlock() {
+ // $this->dumpBlocks();
+
+ $this->assertTrue( $this->block->equals( Block::newFromTarget('UTBlockee') ), "newFromTarget() returns the same block as the one that was made");
+
+ $this->assertTrue( $this->block->equals( Block::newFromID( $this->blockId ) ), "newFromID() returns the same block as the one that was made");
+
+ }
+
+ /**
+ * per bug 26425
+ */
+ function testBug26425BlockTimestampDefaultsToTime() {
+
+ $this->assertEquals( $this->madeAt, $this->block->mTimestamp, "If no timestamp is specified, the block is recorded as time()");
+
+ }
+
+ /**
+ * This is the method previously used to load block info in CheckUser etc
+ * passing an empty value (empty string, null, etc) as the ip parameter bypasses IP lookup checks.
+ *
+ * This stopped working with r84475 and friends: regression being fixed for bug 29116.
+ *
+ * @dataProvider dataBug29116
+ */
+ function testBug29116LoadWithEmptyIp( $vagueTarget ) {
+ $uid = User::idFromName( 'UTBlockee' );
+ $this->assertTrue( ($uid > 0), 'Must be able to look up the target user during tests' );
+
+ $block = new Block();
+ $ok = $block->load( $vagueTarget, $uid );
+ $this->assertTrue( $ok, "Block->load() with empty IP and user ID '$uid' should return a block" );
+
+ $this->assertTrue( $this->block->equals( $block ), "Block->load() returns the same block as the one that was made when given empty ip param " . var_export( $vagueTarget, true ) );
+ }
+
+ /**
+ * CheckUser since being changed to use Block::newFromTarget started failing
+ * because the new function didn't accept empty strings like Block::load()
+ * had. Regression bug 29116.
+ *
+ * @dataProvider dataBug29116
+ */
+ function testBug29116NewFromTargetWithEmptyIp( $vagueTarget ) {
+ $block = Block::newFromTarget('UTBlockee', $vagueTarget);
+ $this->assertTrue( $this->block->equals( $block ), "newFromTarget() returns the same block as the one that was made when given empty vagueTarget param " . var_export( $vagueTarget, true ) );
+ }
+
+ function dataBug29116() {
+ return array(
+ array( null ),
+ array( '' ),
+ array( false )
+ );
+ }
+}
+
diff --git a/tests/phpunit/includes/CdbTest.php b/tests/phpunit/includes/CdbTest.php
new file mode 100644
index 00000000..6c3e6664
--- /dev/null
+++ b/tests/phpunit/includes/CdbTest.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * Test the CDB reader/writer
+ */
+
+class CdbTest extends MediaWikiTestCase {
+
+ public function setUp() {
+ if ( !CdbReader::haveExtension() ) {
+ $this->markTestIncomplete( 'This test requires native CDB support to be present.' );
+ }
+ }
+
+ public function testCdb() {
+ $dir = wfTempDir();
+ if ( !is_writable( $dir ) ) {
+ $this->markTestSkipped( "Temp dir isn't writable" );
+ }
+
+ $w1 = new CdbWriter_PHP( "$dir/php.cdb" );
+ $w2 = new CdbWriter_DBA( "$dir/dba.cdb" );
+
+ $data = array();
+ for ( $i = 0; $i < 1000; $i++ ) {
+ $key = $this->randomString();
+ $value = $this->randomString();
+ $w1->set( $key, $value );
+ $w2->set( $key, $value );
+
+ if ( !isset( $data[$key] ) ) {
+ $data[$key] = $value;
+ }
+ }
+
+ $w1->close();
+ $w2->close();
+
+ $this->assertEquals(
+ md5_file( "$dir/dba.cdb" ),
+ md5_file( "$dir/php.cdb" ),
+ 'same hash'
+ );
+
+ $r1 = new CdbReader_PHP( "$dir/php.cdb" );
+ $r2 = new CdbReader_DBA( "$dir/dba.cdb" );
+
+ foreach ( $data as $key => $value ) {
+ if ( $key === '' ) {
+ // Known bug
+ continue;
+ }
+ $v1 = $r1->get( $key );
+ $v2 = $r2->get( $key );
+
+ $v1 = $v1 === false ? '(not found)' : $v1;
+ $v2 = $v2 === false ? '(not found)' : $v2;
+
+ # cdbAssert( 'Mismatch', $key, $v1, $v2 );
+ $this->cdbAssert( "PHP error", $key, $v1, $value );
+ $this->cdbAssert( "DBA error", $key, $v2, $value );
+ }
+
+ unlink( "$dir/dba.cdb" );
+ unlink( "$dir/php.cdb" );
+ }
+
+ private function randomString() {
+ $len = mt_rand( 0, 10 );
+ $s = '';
+ for ( $j = 0; $j < $len; $j++ ) {
+ $s .= chr( mt_rand( 0, 255 ) );
+ }
+ return $s;
+ }
+
+ private function cdbAssert( $msg, $key, $v1, $v2 ) {
+ $this->assertEquals(
+ $v2,
+ $v1,
+ $msg . ', k=' . bin2hex( $key )
+ );
+ }
+}
diff --git a/tests/phpunit/includes/ExternalStoreTest.php b/tests/phpunit/includes/ExternalStoreTest.php
new file mode 100644
index 00000000..92ec7344
--- /dev/null
+++ b/tests/phpunit/includes/ExternalStoreTest.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * External Store tests
+ */
+
+class ExternalStoreTest extends MediaWikiTestCase {
+ private $saved_wgExternalStores;
+
+ function setUp() {
+ global $wgExternalStores;
+ $this->saved_wgExternalStores = $wgExternalStores ;
+ }
+
+ function tearDown() {
+ global $wgExternalStores;
+ $wgExternalStores = $this->saved_wgExternalStores ;
+ }
+
+ function testExternalStoreDoesNotFetchIncorrectURL() {
+ global $wgExternalStores;
+ $wgExternalStores = true;
+
+ # Assertions for r68900
+ $this->assertFalse(
+ ExternalStore::fetchFromURL( 'http://' ) );
+ $this->assertFalse(
+ ExternalStore::fetchFromURL( 'ftp.wikimedia.org' ) );
+ $this->assertFalse(
+ ExternalStore::fetchFromURL( '/super.txt' ) );
+ }
+}
+
diff --git a/tests/phpunit/includes/ExtraParserTest.php b/tests/phpunit/includes/ExtraParserTest.php
new file mode 100644
index 00000000..5b0aa98b
--- /dev/null
+++ b/tests/phpunit/includes/ExtraParserTest.php
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * Parser-related tests that don't suit for parserTests.txt
+ */
+class ExtraParserTest extends MediaWikiTestCase {
+
+ function setUp() {
+ global $wgMemc;
+ global $wgContLang;
+ global $wgShowDBErrorBacktrace;
+ global $wgLanguageCode;
+
+ $wgShowDBErrorBacktrace = true;
+ $wgLanguageCode = 'en';
+ $wgContLang = new Language( 'en' );
+ $wgMemc = new EmptyBagOStuff;
+
+ $this->options = new ParserOptions;
+ $this->options->setTemplateCallback( array( __CLASS__, 'statelessFetchTemplate' ) );
+ $this->parser = new Parser;
+ }
+
+ // Bug 8689 - Long numeric lines kill the parser
+ function testBug8689() {
+ global $wgLang;
+ global $wgUser;
+ $longLine = '1.' . str_repeat( '1234567890', 100000 ) . "\n";
+
+ if ( $wgLang === null ) $wgLang = new Language;
+
+ $t = Title::newFromText( 'Unit test' );
+ $options = ParserOptions::newFromUser( $wgUser );
+ $this->assertEquals( "<p>$longLine</p>",
+ $this->parser->parse( $longLine, $t, $options )->getText() );
+ }
+
+ /* Test the parser entry points */
+ function testParse() {
+ $title = Title::newFromText( __FUNCTION__ );
+ $parserOutput = $this->parser->parse( "Test\n{{Foo}}\n{{Bar}}" , $title, $this->options );
+ $this->assertEquals( "<p>Test\nContent of <i>Template:Foo</i>\nContent of <i>Template:Bar</i>\n</p>", $parserOutput->getText() );
+ }
+
+ function testPreSaveTransform() {
+ global $wgUser;
+ $title = Title::newFromText( __FUNCTION__ );
+ $outputText = $this->parser->preSaveTransform( "Test\r\n{{subst:Foo}}\n{{Bar}}", $title, $wgUser, $this->options );
+
+ $this->assertEquals( "Test\nContent of ''Template:Foo''\n{{Bar}}", $outputText );
+ }
+
+ function testPreprocess() {
+ $title = Title::newFromText( __FUNCTION__ );
+ $outputText = $this->parser->preprocess( "Test\n{{Foo}}\n{{Bar}}" , $title, $this->options );
+
+ $this->assertEquals( "Test\nContent of ''Template:Foo''\nContent of ''Template:Bar''", $outputText );
+ }
+
+ /**
+ * cleanSig() makes all templates substs and removes tildes
+ */
+ function testCleanSig() {
+ $title = Title::newFromText( __FUNCTION__ );
+ $outputText = $this->parser->cleanSig( "{{Foo}} ~~~~" );
+
+ $this->assertEquals( "{{SUBST:Foo}} ", $outputText );
+ }
+
+ /**
+ * cleanSigInSig() just removes tildes
+ */
+ function testCleanSigInSig() {
+ $title = Title::newFromText( __FUNCTION__ );
+ $outputText = $this->parser->cleanSigInSig( "{{Foo}} ~~~~" );
+
+ $this->assertEquals( "{{Foo}} ", $outputText );
+ }
+
+ function testGetSection() {
+ $outputText2 = $this->parser->getSection( "Section 0\n== Heading 1 ==\nSection 1\n=== Heading 2 ===\nSection 2\n== Heading 3 ==\nSection 3\n", 2 );
+ $outputText1 = $this->parser->getSection( "Section 0\n== Heading 1 ==\nSection 1\n=== Heading 2 ===\nSection 2\n== Heading 3 ==\nSection 3\n", 1 );
+
+ $this->assertEquals( "=== Heading 2 ===\nSection 2", $outputText2 );
+ $this->assertEquals( "== Heading 1 ==\nSection 1\n=== Heading 2 ===\nSection 2", $outputText1 );
+ }
+
+ function testReplaceSection() {
+ $outputText = $this->parser->replaceSection( "Section 0\n== Heading 1 ==\nSection 1\n=== Heading 2 ===\nSection 2\n== Heading 3 ==\nSection 3\n", 1, "New section 1" );
+
+ $this->assertEquals( "Section 0\nNew section 1\n\n== Heading 3 ==\nSection 3", $outputText );
+ }
+
+ /**
+ * Templates and comments are not affected, but noinclude/onlyinclude is.
+ */
+ function testGetPreloadText() {
+ $title = Title::newFromText( __FUNCTION__ );
+ $outputText = $this->parser->getPreloadText( "{{Foo}}<noinclude> censored</noinclude> information <!-- is very secret -->", $title, $this->options );
+
+ $this->assertEquals( "{{Foo}} information <!-- is very secret -->", $outputText );
+ }
+
+ static function statelessFetchTemplate( $title, $parser=false ) {
+ $text = "Content of ''" . $title->getFullText() . "''";
+ $deps = array();
+
+ return array(
+ 'text' => $text,
+ 'finalTitle' => $title,
+ 'deps' => $deps );
+ }
+ }
diff --git a/tests/phpunit/includes/FauxResponseTest.php b/tests/phpunit/includes/FauxResponseTest.php
new file mode 100644
index 00000000..c0420049
--- /dev/null
+++ b/tests/phpunit/includes/FauxResponseTest.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Tests for the FauxResponse class
+ *
+ * Copyright @ 2011 Alexandre Emsenhuber
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+class FauxResponseTest extends MediaWikiTestCase {
+ var $response;
+
+ function setUp() {
+ $this->response = new FauxResponse;
+ }
+
+ function testCookie() {
+ $this->assertEquals( null, $this->response->getcookie( 'key' ), 'Non-existing cookie' );
+ $this->response->setcookie( 'key', 'val' );
+ $this->assertEquals( 'val', $this->response->getcookie( 'key' ), 'Existing cookie' );
+ }
+
+ function testHeader() {
+ $this->assertEquals( null, $this->response->getheader( 'Location' ), 'Non-existing header' );
+
+ $this->response->header( 'Location: http://localhost/' );
+ $this->assertEquals( 'http://localhost/', $this->response->getheader( 'Location' ), 'Set header' );
+
+ $this->response->header( 'Location: http://127.0.0.1/' );
+ $this->assertEquals( 'http://127.0.0.1/', $this->response->getheader( 'Location' ), 'Same header' );
+
+ $this->response->header( 'Location: http://127.0.0.2/', false );
+ $this->assertEquals( 'http://127.0.0.1/', $this->response->getheader( 'Location' ), 'Same header with override disabled' );
+ }
+
+ function testResponseCode() {
+ $this->response->header( 'HTTP/1.1 200' );
+ $this->assertEquals( 200, $this->response->getStatusCode(), 'Header with no message' );
+
+ $this->response->header( 'HTTP/1.x 201' );
+ $this->assertEquals( 201, $this->response->getStatusCode(), 'Header with no message and protocol 1.x' );
+
+ $this->response->header( 'HTTP/1.1 202 OK' );
+ $this->assertEquals( 202, $this->response->getStatusCode(), 'Normal header' );
+
+ $this->response->header( 'HTTP/1.x 203 OK' );
+ $this->assertEquals( 203, $this->response->getStatusCode(), 'Normal header with no message and protocol 1.x' );
+
+ $this->response->header( 'HTTP/1.x 204 OK', false, 205 );
+ $this->assertEquals( 205, $this->response->getStatusCode(), 'Third parameter overrides the HTTP/... header' );
+
+ $this->response->header( 'Location: http://localhost/', false, 206 );
+ $this->assertEquals( 206, $this->response->getStatusCode(), 'Third parameter with another header' );
+ }
+}
diff --git a/tests/phpunit/includes/FormOptionsInitializationTest.php b/tests/phpunit/includes/FormOptionsInitializationTest.php
new file mode 100644
index 00000000..85d76271
--- /dev/null
+++ b/tests/phpunit/includes/FormOptionsInitializationTest.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * This file host two test case classes for the MediaWiki FormOptions class:
+ * - FormOptionsInitializationTest : tests initialization of the class.
+ * - FormOptionsTest : tests methods an on instance
+ *
+ * The split let us take advantage of setting up a fixture for the methods
+ * tests.
+ */
+
+/**
+ * Dummy class to makes FormOptions::$options public.
+ * Used by FormOptionsInitializationTest which need to verify the $options
+ * array is correctly set through the FormOptions::add() function.
+ */
+class FormOptionsExposed extends FormOptions {
+ public function getOptions() {
+ return $this->options;
+ }
+}
+
+/**
+ * Test class for FormOptions initialization
+ * Ensure the FormOptions::add() does what we want it to do.
+ *
+ * Generated by PHPUnit on 2011-02-28 at 20:46:27.
+ *
+ * Copyright © 2011, Ashar Voultoiz
+ *
+ * @author Ashar Voultoiz
+ */
+class FormOptionsInitializationTest extends MediaWikiTestCase {
+ /**
+ * @var FormOptions
+ */
+ protected $object;
+
+
+ /**
+ * A new fresh and empty FormOptions object to test initialization
+ * with.
+ */
+ protected function setUp() {
+ $this->object = new FormOptionsExposed();
+
+ }
+
+ public function testAddStringOption() {
+ $this->object->add( 'foo', 'string value' );
+ $this->assertEquals(
+ array(
+ 'foo' => array(
+ 'default' => 'string value',
+ 'consumed' => false,
+ 'type' => FormOptions::STRING,
+ 'value' => null,
+ )
+ ),
+ $this->object->getOptions()
+ );
+ }
+
+ public function testAddIntegers() {
+ $this->object->add( 'one', 1 );
+ $this->object->add( 'negone', -1 );
+ $this->assertEquals(
+ array(
+ 'negone' => array(
+ 'default' => -1,
+ 'value' => null,
+ 'consumed' => false,
+ 'type' => FormOptions::INT,
+ ),
+ 'one' => array(
+ 'default' => 1,
+ 'value' => null,
+ 'consumed' => false,
+ 'type' => FormOptions::INT,
+ )
+ ),
+ $this->object->getOptions()
+ );
+ }
+
+}
diff --git a/tests/phpunit/includes/FormOptionsTest.php b/tests/phpunit/includes/FormOptionsTest.php
new file mode 100644
index 00000000..86618d93
--- /dev/null
+++ b/tests/phpunit/includes/FormOptionsTest.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * This file host two test case classes for the MediaWiki FormOptions class:
+ * - FormOptionsInitializationTest : tests initialization of the class.
+ * - FormOptionsTest : tests methods an on instance
+ *
+ * The split let us take advantage of setting up a fixture for the methods
+ * tests.
+ */
+
+/**
+ * Test class for FormOptions methods.
+ * Generated by PHPUnit on 2011-02-28 at 20:46:27.
+ *
+ * Copyright © 2011, Ashar Voultoiz
+ *
+ * @author Ashar Voultoiz
+ */
+class FormOptionsTest extends MediaWikiTestCase {
+ /**
+ * @var FormOptions
+ */
+ protected $object;
+
+ /**
+ * Instanciates a FormOptions object to play with.
+ * FormOptions::add() is tested by the class FormOptionsInitializationTest
+ * so we assume the function is well tested already an use it to create
+ * the fixture.
+ */
+ protected function setUp() {
+ $this->object = new FormOptions;
+ $this->object->add( 'string1', 'string one' );
+ $this->object->add( 'string2', 'string two' );
+ $this->object->add( 'integer', 0 );
+ $this->object->add( 'intnull', 0, FormOptions::INTNULL );
+ }
+
+ /** Helpers for testGuessType() */
+ /* @{ */
+ private function assertGuessBoolean( $data ) {
+ $this->guess( FormOptions::BOOL, $data );
+ }
+ private function assertGuessInt( $data ) {
+ $this->guess( FormOptions::INT, $data );
+ }
+ private function assertGuessString( $data ) {
+ $this->guess( FormOptions::STRING, $data );
+ }
+
+ /** Generic helper */
+ private function guess( $expected, $data ) {
+ $this->assertEquals(
+ $expected,
+ FormOptions::guessType( $data )
+ );
+ }
+ /* @} */
+
+ /**
+ * Reuse helpers above assertGuessBoolean assertGuessInt assertGuessString
+ */
+ public function testGuessTypeDetection() {
+ $this->assertGuessBoolean( true );
+ $this->assertGuessBoolean( false );
+
+ $this->assertGuessInt( 0 );
+ $this->assertGuessInt( -5 );
+ $this->assertGuessInt( 5 );
+ $this->assertGuessInt( 0x0F );
+
+ $this->assertGuessString( 'true' );
+ $this->assertGuessString( 'false' );
+ $this->assertGuessString( '5' );
+ $this->assertGuessString( '0' );
+ }
+
+ /**
+ * @expectedException MWException
+ */
+ public function testGuessTypeOnArrayThrowException() {
+ $this->object->guessType( array( 'foo' ) );
+ }
+ /**
+ * @expectedException MWException
+ */
+ public function testGuessTypeOnNullThrowException() {
+ $this->object->guessType( null );
+ }
+}
diff --git a/tests/phpunit/includes/GlobalFunctions/GlobalTest.php b/tests/phpunit/includes/GlobalFunctions/GlobalTest.php
new file mode 100644
index 00000000..3d157d0a
--- /dev/null
+++ b/tests/phpunit/includes/GlobalFunctions/GlobalTest.php
@@ -0,0 +1,902 @@
+<?php
+
+class GlobalTest extends MediaWikiTestCase {
+ function setUp() {
+ global $wgReadOnlyFile, $wgUrlProtocols;
+ $this->originals['wgReadOnlyFile'] = $wgReadOnlyFile;
+ $this->originals['wgUrlProtocols'] = $wgUrlProtocols;
+ $wgReadOnlyFile = tempnam( wfTempDir(), "mwtest_readonly" );
+ $wgUrlProtocols[] = 'file://';
+ unlink( $wgReadOnlyFile );
+ }
+
+ function tearDown() {
+ global $wgReadOnlyFile, $wgUrlProtocols;
+ if ( file_exists( $wgReadOnlyFile ) ) {
+ unlink( $wgReadOnlyFile );
+ }
+ $wgReadOnlyFile = $this->originals['wgReadOnlyFile'];
+ $wgUrlProtocols = $this->originals['wgUrlProtocols'];
+ }
+
+ /** @dataProvider provideForWfArrayDiff2 */
+ public function testWfArrayDiff2( $a, $b, $expected ) {
+ $this->assertEquals(
+ wfArrayDiff2( $a, $b), $expected
+ );
+ }
+
+ // @todo Provide more tests
+ public function provideForWfArrayDiff2() {
+ // $a $b $expected
+ return array(
+ array(
+ array( 'a', 'b'),
+ array( 'a', 'b'),
+ array(),
+ ),
+ array(
+ array( array( 'a'), array( 'a', 'b', 'c' )),
+ array( array( 'a'), array( 'a', 'b' )),
+ array( 1 => array( 'a', 'b', 'c' ) ),
+ ),
+ );
+ }
+
+ function testRandom() {
+ # This could hypothetically fail, but it shouldn't ;)
+ $this->assertFalse(
+ wfRandom() == wfRandom() );
+ }
+
+ function testUrlencode() {
+ $this->assertEquals(
+ "%E7%89%B9%E5%88%A5:Contributions/Foobar",
+ wfUrlencode( "\xE7\x89\xB9\xE5\x88\xA5:Contributions/Foobar" ) );
+ }
+
+ function testReadOnlyEmpty() {
+ global $wgReadOnly;
+ $wgReadOnly = null;
+
+ $this->assertFalse( wfReadOnly() );
+ $this->assertFalse( wfReadOnly() );
+ }
+
+ function testReadOnlySet() {
+ global $wgReadOnly, $wgReadOnlyFile;
+
+ $f = fopen( $wgReadOnlyFile, "wt" );
+ fwrite( $f, 'Message' );
+ fclose( $f );
+ $wgReadOnly = null; # Check on $wgReadOnlyFile
+
+ $this->assertTrue( wfReadOnly() );
+ $this->assertTrue( wfReadOnly() ); # Check cached
+
+ unlink( $wgReadOnlyFile );
+ $wgReadOnly = null; # Clean cache
+
+ $this->assertFalse( wfReadOnly() );
+ $this->assertFalse( wfReadOnly() );
+ }
+
+ function testQuotedPrintable() {
+ $this->assertEquals(
+ "=?UTF-8?Q?=C4=88u=20legebla=3F?=",
+ UserMailer::quotedPrintable( "\xc4\x88u legebla?", "UTF-8" ) );
+ }
+
+ function testTime() {
+ $start = wfTime();
+ $this->assertInternalType( 'float', $start );
+ $end = wfTime();
+ $this->assertTrue( $end > $start, "Time is running backwards!" );
+ }
+
+ function testArrayToCGI() {
+ $this->assertEquals(
+ "baz=AT%26T&foo=bar",
+ wfArrayToCGI(
+ array( 'baz' => 'AT&T', 'ignore' => '' ),
+ array( 'foo' => 'bar', 'baz' => 'overridden value' ) ) );
+ $this->assertEquals(
+ "path%5B0%5D=wiki&path%5B1%5D=test&cfg%5Bservers%5D%5Bhttp%5D=localhost",
+ wfArrayToCGI( array(
+ 'path' => array( 'wiki', 'test' ),
+ 'cfg' => array( 'servers' => array( 'http' => 'localhost' ) ) ) ) );
+ }
+
+ function testCgiToArray() {
+ $this->assertEquals(
+ array( 'path' => array( 'wiki', 'test' ),
+ 'cfg' => array( 'servers' => array( 'http' => 'localhost' ) ) ),
+ wfCgiToArray( 'path%5B0%5D=wiki&path%5B1%5D=test&cfg%5Bservers%5D%5Bhttp%5D=localhost' ) );
+ }
+
+ function testMimeTypeMatch() {
+ $this->assertEquals(
+ 'text/html',
+ mimeTypeMatch( 'text/html',
+ array( 'application/xhtml+xml' => 1.0,
+ 'text/html' => 0.7,
+ 'text/plain' => 0.3 ) ) );
+ $this->assertEquals(
+ 'text/*',
+ mimeTypeMatch( 'text/html',
+ array( 'image/*' => 1.0,
+ 'text/*' => 0.5 ) ) );
+ $this->assertEquals(
+ '*/*',
+ mimeTypeMatch( 'text/html',
+ array( '*/*' => 1.0 ) ) );
+ $this->assertNull(
+ mimeTypeMatch( 'text/html',
+ array( 'image/png' => 1.0,
+ 'image/svg+xml' => 0.5 ) ) );
+ }
+
+ function testNegotiateType() {
+ $this->assertEquals(
+ 'text/html',
+ wfNegotiateType(
+ array( 'application/xhtml+xml' => 1.0,
+ 'text/html' => 0.7,
+ 'text/plain' => 0.5,
+ 'text/*' => 0.2 ),
+ array( 'text/html' => 1.0 ) ) );
+ $this->assertEquals(
+ 'application/xhtml+xml',
+ wfNegotiateType(
+ array( 'application/xhtml+xml' => 1.0,
+ 'text/html' => 0.7,
+ 'text/plain' => 0.5,
+ 'text/*' => 0.2 ),
+ array( 'application/xhtml+xml' => 1.0,
+ 'text/html' => 0.5 ) ) );
+ $this->assertEquals(
+ 'text/html',
+ wfNegotiateType(
+ array( 'text/html' => 1.0,
+ 'text/plain' => 0.5,
+ 'text/*' => 0.5,
+ 'application/xhtml+xml' => 0.2 ),
+ array( 'application/xhtml+xml' => 1.0,
+ 'text/html' => 0.5 ) ) );
+ $this->assertEquals(
+ 'text/html',
+ wfNegotiateType(
+ array( 'text/*' => 1.0,
+ 'image/*' => 0.7,
+ '*/*' => 0.3 ),
+ array( 'application/xhtml+xml' => 1.0,
+ 'text/html' => 0.5 ) ) );
+ $this->assertNull(
+ wfNegotiateType(
+ array( 'text/*' => 1.0 ),
+ array( 'application/xhtml+xml' => 1.0 ) ) );
+ }
+
+ function testTimestamp() {
+ $t = gmmktime( 12, 34, 56, 1, 15, 2001 );
+ $this->assertEquals(
+ '20010115123456',
+ wfTimestamp( TS_MW, $t ),
+ 'TS_UNIX to TS_MW' );
+ $this->assertEquals(
+ '19690115123456',
+ wfTimestamp( TS_MW, -30281104 ),
+ 'Negative TS_UNIX to TS_MW' );
+ $this->assertEquals(
+ 979562096,
+ wfTimestamp( TS_UNIX, $t ),
+ 'TS_UNIX to TS_UNIX' );
+ $this->assertEquals(
+ '2001-01-15 12:34:56',
+ wfTimestamp( TS_DB, $t ),
+ 'TS_UNIX to TS_DB' );
+ $this->assertEquals(
+ '20010115T123456Z',
+ wfTimestamp( TS_ISO_8601_BASIC, $t ),
+ 'TS_ISO_8601_BASIC to TS_DB' );
+
+ $this->assertEquals(
+ '20010115123456',
+ wfTimestamp( TS_MW, '20010115123456' ),
+ 'TS_MW to TS_MW' );
+ $this->assertEquals(
+ 979562096,
+ wfTimestamp( TS_UNIX, '20010115123456' ),
+ 'TS_MW to TS_UNIX' );
+ $this->assertEquals(
+ '2001-01-15 12:34:56',
+ wfTimestamp( TS_DB, '20010115123456' ),
+ 'TS_MW to TS_DB' );
+ $this->assertEquals(
+ '20010115T123456Z',
+ wfTimestamp( TS_ISO_8601_BASIC, '20010115123456' ),
+ 'TS_MW to TS_ISO_8601_BASIC' );
+
+ $this->assertEquals(
+ '20010115123456',
+ wfTimestamp( TS_MW, '2001-01-15 12:34:56' ),
+ 'TS_DB to TS_MW' );
+ $this->assertEquals(
+ 979562096,
+ wfTimestamp( TS_UNIX, '2001-01-15 12:34:56' ),
+ 'TS_DB to TS_UNIX' );
+ $this->assertEquals(
+ '2001-01-15 12:34:56',
+ wfTimestamp( TS_DB, '2001-01-15 12:34:56' ),
+ 'TS_DB to TS_DB' );
+ $this->assertEquals(
+ '20010115T123456Z',
+ wfTimestamp( TS_ISO_8601_BASIC, '2001-01-15 12:34:56' ),
+ 'TS_DB to TS_ISO_8601_BASIC' );
+
+ # rfc2822 section 3.3
+
+ $this->assertEquals(
+ 'Mon, 15 Jan 2001 12:34:56 GMT',
+ wfTimestamp( TS_RFC2822, '20010115123456' ),
+ 'TS_MW to TS_RFC2822' );
+
+ $this->assertEquals(
+ '20010115123456',
+ wfTimestamp( TS_MW, 'Mon, 15 Jan 2001 12:34:56 GMT' ),
+ 'TS_RFC2822 to TS_MW' );
+
+ $this->assertEquals(
+ '20010115123456',
+ wfTimestamp( TS_MW, ' Mon, 15 Jan 2001 12:34:56 GMT' ),
+ 'TS_RFC2822 with leading space to TS_MW' );
+
+ $this->assertEquals(
+ '20010115123456',
+ wfTimestamp( TS_MW, '15 Jan 2001 12:34:56 GMT' ),
+ 'TS_RFC2822 without optional day-of-week to TS_MW' );
+
+ # FWS = ([*WSP CRLF] 1*WSP) / obs-FWS ; Folding white space
+ # obs-FWS = 1*WSP *(CRLF 1*WSP) ; Section 4.2
+ $this->assertEquals(
+ '20010115123456',
+ wfTimestamp( TS_MW, 'Mon, 15 Jan 2001 12:34:56 GMT' ),
+ 'TS_RFC2822 to TS_MW' );
+
+ # WSP = SP / HTAB ; rfc2234
+ $this->assertEquals(
+ '20010115123456',
+ wfTimestamp( TS_MW, "Mon, 15 Jan\x092001 12:34:56 GMT" ),
+ 'TS_RFC2822 with HTAB to TS_MW' );
+
+ $this->assertEquals(
+ '20010115123456',
+ wfTimestamp( TS_MW, "Mon, 15 Jan\x09 \x09 2001 12:34:56 GMT" ),
+ 'TS_RFC2822 with HTAB and SP to TS_MW' );
+
+ $this->assertEquals(
+ '19941106084937',
+ wfTimestamp( TS_MW, "Sun, 6 Nov 94 08:49:37 GMT" ),
+ 'TS_RFC2822 with obsolete year to TS_MW' );
+ }
+
+ /**
+ * This test checks wfTimestamp() with values outside.
+ * It needs PHP 64 bits or PHP > 5.1.
+ * See r74778 and bug 25451
+ */
+ function testOldTimestamps() {
+ $this->assertEquals( 'Fri, 13 Dec 1901 20:45:54 GMT',
+ wfTimestamp( TS_RFC2822, '19011213204554' ),
+ 'Earliest time according to php documentation' );
+
+ $this->assertEquals( 'Tue, 19 Jan 2038 03:14:07 GMT',
+ wfTimestamp( TS_RFC2822, '20380119031407' ),
+ 'Latest 32 bit time' );
+
+ $this->assertEquals( '-2147483648',
+ wfTimestamp( TS_UNIX, '19011213204552' ),
+ 'Earliest 32 bit unix time' );
+
+ $this->assertEquals( '2147483647',
+ wfTimestamp( TS_UNIX, '20380119031407' ),
+ 'Latest 32 bit unix time' );
+
+ $this->assertEquals( 'Fri, 13 Dec 1901 20:45:52 GMT',
+ wfTimestamp( TS_RFC2822, '19011213204552' ),
+ 'Earliest 32 bit time' );
+
+ $this->assertEquals( 'Fri, 13 Dec 1901 20:45:51 GMT',
+ wfTimestamp( TS_RFC2822, '19011213204551' ),
+ 'Earliest 32 bit time - 1' );
+
+ $this->assertEquals( 'Tue, 19 Jan 2038 03:14:08 GMT',
+ wfTimestamp( TS_RFC2822, '20380119031408' ),
+ 'Latest 32 bit time + 1' );
+
+ $this->assertEquals( '19011212000000',
+ wfTimestamp(TS_MW, '19011212000000'),
+ 'Convert to itself r74778#c10645' );
+
+ $this->assertEquals( '-2147483649',
+ wfTimestamp( TS_UNIX, '19011213204551' ),
+ 'Earliest 32 bit unix time - 1' );
+
+ $this->assertEquals( '2147483648',
+ wfTimestamp( TS_UNIX, '20380119031408' ),
+ 'Latest 32 bit unix time + 1' );
+
+ $this->assertEquals( '19011213204551',
+ wfTimestamp( TS_MW, '-2147483649' ),
+ '1901 negative unix time to MediaWiki' );
+
+ $this->assertEquals( '18010115123456',
+ wfTimestamp( TS_MW, '-5331871504' ),
+ '1801 negative unix time to MediaWiki' );
+
+ $this->assertEquals( 'Tue, 09 Aug 0117 12:34:56 GMT',
+ wfTimestamp( TS_RFC2822, '0117-08-09 12:34:56'),
+ 'Death of Roman Emperor [[Trajan]]');
+
+ /* @todo FIXME: 00 to 101 years are taken as being in [1970-2069] */
+
+ $this->assertEquals( 'Sun, 01 Jan 0101 00:00:00 GMT',
+ wfTimestamp( TS_RFC2822, '-58979923200'),
+ '1/1/101');
+
+ $this->assertEquals( 'Mon, 01 Jan 0001 00:00:00 GMT',
+ wfTimestamp( TS_RFC2822, '-62135596800'),
+ 'Year 1');
+
+ /* It is not clear if we should generate a year 0 or not
+ * We are completely off RFC2822 requirement of year being
+ * 1900 or later.
+ */
+ $this->assertEquals( 'Wed, 18 Oct 0000 00:00:00 GMT',
+ wfTimestamp( TS_RFC2822, '-62142076800'),
+ 'ISO 8601:2004 [[year 0]], also called [[1 BC]]');
+ }
+
+ function testHttpDate() {
+ # The Resource Loader uses wfTimestamp() to convert timestamps
+ # from If-Modified-Since header.
+ # Thus it must be able to parse all rfc2616 date formats
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
+
+ $this->assertEquals(
+ '19941106084937',
+ wfTimestamp( TS_MW, 'Sun, 06 Nov 1994 08:49:37 GMT' ),
+ 'RFC 822 date' );
+
+ $this->assertEquals(
+ '19941106084937',
+ wfTimestamp( TS_MW, 'Sunday, 06-Nov-94 08:49:37 GMT' ),
+ 'RFC 850 date' );
+
+ $this->assertEquals(
+ '19941106084937',
+ wfTimestamp( TS_MW, 'Sun Nov 6 08:49:37 1994' ),
+ "ANSI C's asctime() format" );
+
+ // See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html and r77171
+ $this->assertEquals(
+ '20101122141242',
+ wfTimestamp( TS_MW, 'Mon, 22 Nov 2010 14:12:42 GMT; length=52626' ),
+ "Netscape extension to HTTP/1.0" );
+
+ }
+
+ function testTimestampParameter() {
+ // There are a number of assumptions in our codebase where wfTimestamp() should give
+ // the current date but it is not given a 0 there. See r71751 CR
+
+ $now = wfTimestamp( TS_UNIX );
+ // We check that wfTimestamp doesn't return false (error) and use a LessThan assert
+ // for the cases where the test is run in a second boundary.
+
+ $zero = wfTimestamp( TS_UNIX, 0 );
+ $this->assertNotEquals( false, $zero );
+ $this->assertLessThan( 5, $zero - $now );
+
+ $empty = wfTimestamp( TS_UNIX, '' );
+ $this->assertNotEquals( false, $empty );
+ $this->assertLessThan( 5, $empty - $now );
+
+ $null = wfTimestamp( TS_UNIX, null );
+ $this->assertNotEquals( false, $null );
+ $this->assertLessThan( 5, $null - $now );
+ }
+
+ function testBasename() {
+ $sets = array(
+ '' => '',
+ '/' => '',
+ '\\' => '',
+ '//' => '',
+ '\\\\' => '',
+ 'a' => 'a',
+ 'aaaa' => 'aaaa',
+ '/a' => 'a',
+ '\\a' => 'a',
+ '/aaaa' => 'aaaa',
+ '\\aaaa' => 'aaaa',
+ '/aaaa/' => 'aaaa',
+ '\\aaaa\\' => 'aaaa',
+ '\\aaaa\\' => 'aaaa',
+ '/mnt/upload3/wikipedia/en/thumb/8/8b/Zork_Grand_Inquisitor_box_cover.jpg/93px-Zork_Grand_Inquisitor_box_cover.jpg' => '93px-Zork_Grand_Inquisitor_box_cover.jpg',
+ 'C:\\Progra~1\\Wikime~1\\Wikipe~1\\VIEWER.EXE' => 'VIEWER.EXE',
+ 'Östergötland_coat_of_arms.png' => 'Östergötland_coat_of_arms.png',
+ );
+ foreach ( $sets as $from => $to ) {
+ $this->assertEquals( $to, wfBaseName( $from ),
+ "wfBaseName('$from') => '$to'" );
+ }
+ }
+
+
+ function testFallbackMbstringFunctions() {
+
+ if( !extension_loaded( 'mbstring' ) ) {
+ $this->markTestSkipped( "The mb_string functions must be installed to test the fallback functions" );
+ }
+
+ $sampleUTF = "Östergötland_coat_of_arms.png";
+
+
+ //mb_substr
+ $substr_params = array(
+ array( 0, 0 ),
+ array( 5, -4 ),
+ array( 33 ),
+ array( 100, -5 ),
+ array( -8, 10 ),
+ array( 1, 1 ),
+ array( 2, -1 )
+ );
+
+ foreach( $substr_params as $param_set ) {
+ $old_param_set = $param_set;
+ array_unshift( $param_set, $sampleUTF );
+
+ $this->assertEquals(
+ MWFunction::callArray( 'mb_substr', $param_set ),
+ MWFunction::callArray( 'Fallback::mb_substr', $param_set ),
+ 'Fallback mb_substr with params ' . implode( ', ', $old_param_set )
+ );
+ }
+
+
+ //mb_strlen
+ $this->assertEquals(
+ mb_strlen( $sampleUTF ),
+ Fallback::mb_strlen( $sampleUTF ),
+ 'Fallback mb_strlen'
+ );
+
+
+ //mb_str(r?)pos
+ $strpos_params = array(
+ //array( 'ter' ),
+ //array( 'Ö' ),
+ //array( 'Ö', 3 ),
+ //array( 'oat_', 100 ),
+ //array( 'c', -10 ),
+ //Broken for now
+ );
+
+ foreach( $strpos_params as $param_set ) {
+ $old_param_set = $param_set;
+ array_unshift( $param_set, $sampleUTF );
+
+ $this->assertEquals(
+ MWFunction::callArray( 'mb_strpos', $param_set ),
+ MWFunction::callArray( 'Fallback::mb_strpos', $param_set ),
+ 'Fallback mb_strpos with params ' . implode( ', ', $old_param_set )
+ );
+
+ $this->assertEquals(
+ MWFunction::callArray( 'mb_strrpos', $param_set ),
+ MWFunction::callArray( 'Fallback::mb_strrpos', $param_set ),
+ 'Fallback mb_strrpos with params ' . implode( ', ', $old_param_set )
+ );
+ }
+
+ }
+
+
+ function testDebugFunctionTest() {
+
+ global $wgDebugLogFile, $wgOut, $wgShowDebug, $wgDebugTimestamps;
+
+ $old_log_file = $wgDebugLogFile;
+ $wgDebugLogFile = tempnam( wfTempDir(), 'mw-' );
+ # @todo FIXME: This setting should be tested
+ $wgDebugTimestamps = false;
+
+
+
+ wfDebug( "This is a normal string" );
+ $this->assertEquals( "This is a normal string", file_get_contents( $wgDebugLogFile ) );
+ unlink( $wgDebugLogFile );
+
+
+ wfDebug( "This is nöt an ASCII string" );
+ $this->assertEquals( "This is nöt an ASCII string", file_get_contents( $wgDebugLogFile ) );
+ unlink( $wgDebugLogFile );
+
+
+ wfDebug( "\00305This has böth UTF and control chars\003" );
+ $this->assertEquals( " 05This has böth UTF and control chars ", file_get_contents( $wgDebugLogFile ) );
+ unlink( $wgDebugLogFile );
+
+
+
+ $old_wgOut = $wgOut;
+ $old_wgShowDebug = $wgShowDebug;
+
+ $wgOut = new MockOutputPage;
+
+ $wgShowDebug = true;
+
+ $message = "\00305This has böth UTF and control chars\003";
+
+ wfDebug( $message );
+
+ if( $wgOut->message == "JAJA is a stupid error message. Anyway, here's your message: $message" ) {
+ $this->assertTrue( true, 'MockOutputPage called, set the proper message.' );
+ }
+ else {
+ $this->assertTrue( false, 'MockOutputPage was not called.' );
+ }
+
+ $wgOut = $old_wgOut;
+ $wgShowDebug = $old_wgShowDebug;
+ unlink( $wgDebugLogFile );
+
+
+
+ wfDebugMem();
+ $this->assertGreaterThan( 5000, preg_replace( '/\D/', '', file_get_contents( $wgDebugLogFile ) ) );
+ unlink( $wgDebugLogFile );
+
+ wfDebugMem(true);
+ $this->assertGreaterThan( 5000000, preg_replace( '/\D/', '', file_get_contents( $wgDebugLogFile ) ) );
+ unlink( $wgDebugLogFile );
+
+
+
+ $wgDebugLogFile = $old_log_file;
+
+ }
+
+ function testClientAcceptsGzipTest() {
+
+ $settings = array(
+ 'gzip' => true,
+ 'bzip' => false,
+ '*' => false,
+ 'compress, gzip' => true,
+ 'gzip;q=1.0' => true,
+ 'foozip' => false,
+ 'foo*zip' => false,
+ 'gzip;q=abcde' => true, //is this REALLY valid?
+ 'gzip;q=12345678.9' => true,
+ ' gzip' => true,
+ );
+
+ if( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) $old_server_setting = $_SERVER['HTTP_ACCEPT_ENCODING'];
+
+ foreach ( $settings as $encoding => $expect ) {
+ $_SERVER['HTTP_ACCEPT_ENCODING'] = $encoding;
+
+ $this->assertEquals( $expect, wfClientAcceptsGzip( true ),
+ "'$encoding' => " . wfBoolToStr( $expect ) );
+ }
+
+ if( isset( $old_server_setting ) ) $_SERVER['HTTP_ACCEPT_ENCODING'] = $old_server_setting;
+
+ }
+
+
+
+ function testSwapVarsTest() {
+
+
+ $var1 = 1;
+ $var2 = 2;
+
+ $this->assertEquals( $var1, 1, 'var1 is set originally' );
+ $this->assertEquals( $var2, 2, 'var1 is set originally' );
+
+ swap( $var1, $var2 );
+
+ $this->assertEquals( $var1, 2, 'var1 is swapped' );
+ $this->assertEquals( $var2, 1, 'var2 is swapped' );
+
+ }
+
+
+ function testWfPercentTest() {
+
+ $pcts = array(
+ array( 6/7, '0.86%', 2, false ),
+ array( 3/3, '1%' ),
+ array( 22/7, '3.14286%', 5 ),
+ array( 3/6, '0.5%' ),
+ array( 1/3, '0%', 0 ),
+ array( 10/3, '0%', -1 ),
+ array( 3/4/5, '0.1%', 1 ),
+ array( 6/7*8, '6.8571428571%', 10 ),
+ );
+
+ foreach( $pcts as $pct ) {
+ if( !isset( $pct[2] ) ) $pct[2] = 2;
+ if( !isset( $pct[3] ) ) $pct[3] = true;
+
+ $this->assertEquals( wfPercent( $pct[0], $pct[2], $pct[3] ), $pct[1], $pct[1] );
+ }
+
+ }
+
+
+ function testInStringTest() {
+
+ $this->assertTrue( in_string( 'foo', 'foobar' ), 'foo is in foobar' );
+ $this->assertFalse( in_string( 'Bar', 'foobar' ), 'Case-sensitive by default' );
+ $this->assertTrue( in_string( 'Foo', 'foobar', true ), 'Case-insensitive when asked' );
+
+ }
+
+ /**
+ * test @see wfShorthandToInteger()
+ * @dataProvider provideShorthand
+ */
+ public function testWfShorthandToInteger( $shorthand, $expected ) {
+ $this->assertEquals( $expected,
+ wfShorthandToInteger( $shorthand )
+ );
+ }
+
+ /** array( shorthand, expected integer ) */
+ public function provideShorthand() {
+ return array(
+ # Null, empty ...
+ array( '', -1),
+ array( ' ', -1),
+ array( null, -1),
+
+ # Failures returns 0 :(
+ array( 'ABCDEFG', 0 ),
+ array( 'Ak', 0 ),
+
+ # Int, strings with spaces
+ array( 1, 1 ),
+ array( ' 1 ', 1 ),
+ array( 1023, 1023 ),
+ array( ' 1023 ', 1023 ),
+
+ # kilo, Mega, Giga
+ array( '1k', 1024 ),
+ array( '1K', 1024 ),
+ array( '1m', 1024 * 1024 ),
+ array( '1M', 1024 * 1024 ),
+ array( '1g', 1024 * 1024 * 1024 ),
+ array( '1G', 1024 * 1024 * 1024 ),
+
+ # Negatives
+ array( -1, -1 ),
+ array( -500, -500 ),
+ array( '-500', -500 ),
+ array( '-1k', -1024 ),
+
+ # Zeroes
+ array( '0', 0 ),
+ array( '0k', 0 ),
+ array( '0M', 0 ),
+ array( '0G', 0 ),
+ array( '-0', 0 ),
+ array( '-0k', 0 ),
+ array( '-0M', 0 ),
+ array( '-0G', 0 ),
+ );
+ }
+
+
+ /**
+ * test @see wfBCP47().
+ * Please note the BCP explicitly state that language codes are case
+ * insensitive, there are some exceptions to the rule :)
+ * This test is used to verify our formatting against all lower and
+ * all upper cases language code.
+ *
+ * @see http://tools.ietf.org/html/bcp47
+ * @dataProvider provideLanguageCodes()
+ */
+ function testBCP47( $code, $expected ) {
+ $code = strtolower( $code );
+ $this->assertEquals( $expected, wfBCP47($code),
+ "Applying BCP47 standard to lower case '$code'"
+ );
+
+ $code = strtoupper( $code );
+ $this->assertEquals( $expected, wfBCP47($code),
+ "Applying BCP47 standard to upper case '$code'"
+ );
+ }
+
+ /**
+ * Array format is ($code, $expected)
+ */
+ function provideLanguageCodes() {
+ return array(
+ // Extracted from BCP47 (list not exhaustive)
+ # 2.1.1
+ array( 'en-ca-x-ca' , 'en-CA-x-ca' ),
+ array( 'sgn-be-fr' , 'sgn-BE-FR' ),
+ array( 'az-latn-x-latn', 'az-Latn-x-latn' ),
+ # 2.2
+ array( 'sr-Latn-RS', 'sr-Latn-RS' ),
+ array( 'az-arab-ir', 'az-Arab-IR' ),
+
+ # 2.2.5
+ array( 'sl-nedis' , 'sl-nedis' ),
+ array( 'de-ch-1996', 'de-CH-1996' ),
+
+ # 2.2.6
+ array(
+ 'en-latn-gb-boont-r-extended-sequence-x-private',
+ 'en-Latn-GB-boont-r-extended-sequence-x-private'
+ ),
+
+ // Examples from BCP47 Appendix A
+ # Simple language subtag:
+ array( 'DE', 'de' ),
+ array( 'fR', 'fr' ),
+ array( 'ja', 'ja' ),
+
+ # Language subtag plus script subtag:
+ array( 'zh-hans', 'zh-Hans'),
+ array( 'sr-cyrl', 'sr-Cyrl'),
+ array( 'sr-latn', 'sr-Latn'),
+
+ # Extended language subtags and their primary language subtag
+ # counterparts:
+ array( 'zh-cmn-hans-cn', 'zh-cmn-Hans-CN' ),
+ array( 'cmn-hans-cn' , 'cmn-Hans-CN' ),
+ array( 'zh-yue-hk' , 'zh-yue-HK' ),
+ array( 'yue-hk' , 'yue-HK' ),
+
+ # Language-Script-Region:
+ array( 'zh-hans-cn', 'zh-Hans-CN' ),
+ array( 'sr-latn-RS', 'sr-Latn-RS' ),
+
+ # Language-Variant:
+ array( 'sl-rozaj' , 'sl-rozaj' ),
+ array( 'sl-rozaj-biske', 'sl-rozaj-biske' ),
+ array( 'sl-nedis' , 'sl-nedis' ),
+
+ # Language-Region-Variant:
+ array( 'de-ch-1901' , 'de-CH-1901' ),
+ array( 'sl-it-nedis' , 'sl-IT-nedis' ),
+
+ # Language-Script-Region-Variant:
+ array( 'hy-latn-it-arevela', 'hy-Latn-IT-arevela' ),
+
+ # Language-Region:
+ array( 'de-de' , 'de-DE' ),
+ array( 'en-us' , 'en-US' ),
+ array( 'es-419', 'es-419'),
+
+ # Private use subtags:
+ array( 'de-ch-x-phonebk' , 'de-CH-x-phonebk' ),
+ array( 'az-arab-x-aze-derbend', 'az-Arab-x-aze-derbend' ),
+ /**
+ * Previous test does not reflect the BCP which states:
+ * az-Arab-x-AZE-derbend
+ * AZE being private, it should be lower case, hence the test above
+ * should probably be:
+ #array( 'az-arab-x-aze-derbend', 'az-Arab-x-AZE-derbend' ),
+ */
+
+ # Private use registry values:
+ array( 'x-whatever', 'x-whatever' ),
+ array( 'qaa-qaaa-qm-x-southern', 'qaa-Qaaa-QM-x-southern' ),
+ array( 'de-qaaa' , 'de-Qaaa' ),
+ array( 'sr-latn-qm', 'sr-Latn-QM' ),
+ array( 'sr-qaaa-rs', 'sr-Qaaa-RS' ),
+
+ # Tags that use extensions
+ array( 'en-us-u-islamcal', 'en-US-u-islamcal' ),
+ array( 'zh-cn-a-myext-x-private', 'zh-CN-a-myext-x-private' ),
+ array( 'en-a-myext-b-another', 'en-a-myext-b-another' ),
+
+ # Invalid:
+ // de-419-DE
+ // a-DE
+ // ar-a-aaa-b-bbb-a-ccc
+
+ /*
+ // ISO 15924 :
+ array( 'sr-Cyrl', 'sr-Cyrl' ),
+ # @todo FIXME: Fix our function?
+ array( 'SR-lATN', 'sr-Latn' ),
+ array( 'fr-latn', 'fr-Latn' ),
+ // Use lowercase for single segment
+ // ISO 3166-1-alpha-2 code
+ array( 'US', 'us' ), # USA
+ array( 'uS', 'us' ), # USA
+ array( 'Fr', 'fr' ), # France
+ array( 'va', 'va' ), # Holy See (Vatican City State)
+ */);
+ }
+
+ /**
+ * @dataProvider provideMakeUrlIndexes()
+ */
+ function testMakeUrlIndexes( $url, $expected ) {
+ $index = wfMakeUrlIndexes( $url );
+ $this->assertEquals( $expected, $index, "wfMakeUrlIndexes(\"$url\")" );
+ }
+
+ function provideMakeUrlIndexes() {
+ return array(
+ array(
+ // just a regular :)
+ 'https://bugzilla.wikimedia.org/show_bug.cgi?id=28627',
+ array( 'https://org.wikimedia.bugzilla./show_bug.cgi?id=28627' )
+ ),
+ array(
+ // mailtos are handled special
+ // is this really right though? that final . probably belongs earlier?
+ 'mailto:wiki@wikimedia.org',
+ array( 'mailto:org.wikimedia@wiki.' )
+ ),
+
+ // file URL cases per bug 28627...
+ array(
+ // three slashes: local filesystem path Unix-style
+ 'file:///whatever/you/like.txt',
+ array( 'file://./whatever/you/like.txt' )
+ ),
+ array(
+ // three slashes: local filesystem path Windows-style
+ 'file:///c:/whatever/you/like.txt',
+ array( 'file://./c:/whatever/you/like.txt' )
+ ),
+ array(
+ // two slashes: UNC filesystem path Windows-style
+ 'file://intranet/whatever/you/like.txt',
+ array( 'file://intranet./whatever/you/like.txt' )
+ ),
+ // Multiple-slash cases that can sorta work on Mozilla
+ // if you hack it just right are kinda pathological,
+ // and unreliable cross-platform or on IE which means they're
+ // unlikely to appear on intranets.
+ //
+ // Those will survive the algorithm but with results that
+ // are less consistent.
+
+ // protocol-relative URL cases per bug 29854...
+ array(
+ '//bugzilla.wikimedia.org/show_bug.cgi?id=28627',
+ array(
+ 'http://org.wikimedia.bugzilla./show_bug.cgi?id=28627',
+ 'https://org.wikimedia.bugzilla./show_bug.cgi?id=28627'
+ )
+ ),
+ );
+ }
+
+ /* TODO: many more! */
+}
+
+
+class MockOutputPage {
+
+ public $message;
+
+ function debug( $message ) {
+ $this->message = "JAJA is a stupid error message. Anyway, here's your message: $message";
+ }
+}
+
diff --git a/tests/phpunit/includes/GlobalFunctions/README b/tests/phpunit/includes/GlobalFunctions/README
new file mode 100644
index 00000000..0042bdac
--- /dev/null
+++ b/tests/phpunit/includes/GlobalFunctions/README
@@ -0,0 +1,2 @@
+This directory hold tests for includes/GlobalFunctions.php file
+which is a pile of functions.
diff --git a/tests/phpunit/includes/GlobalFunctions/wfExpandUrl.php b/tests/phpunit/includes/GlobalFunctions/wfExpandUrl.php
new file mode 100644
index 00000000..b388b266
--- /dev/null
+++ b/tests/phpunit/includes/GlobalFunctions/wfExpandUrl.php
@@ -0,0 +1,78 @@
+<?php
+/*
+ * Unit tests for wfExpandUrl()
+ */
+
+class wfExpandUrl extends MediaWikiTestCase {
+ /** @dataProvider provideExpandableUrls */
+ public function testWfExpandUrl( $fullUrl, $shortUrl, $defaultProto, $server, $canServer, $httpsMode, $message ) {
+ // Fake $wgServer and $wgCanonicalServer
+ global $wgServer, $wgCanonicalServer;
+ $oldServer = $wgServer;
+ $oldCanServer = $wgCanonicalServer;
+ $wgServer = $server;
+ $wgCanonicalServer = $canServer;
+
+ // Fake $_SERVER['HTTPS'] if needed
+ if ( $httpsMode ) {
+ $_SERVER['HTTPS'] = 'on';
+ } else {
+ unset( $_SERVER['HTTPS'] );
+ }
+
+ $this->assertEquals( $fullUrl, wfExpandUrl( $shortUrl, $defaultProto ), $message );
+
+ // Restore $wgServer and $wgCanonicalServer
+ $wgServer = $oldServer;
+ $wgCanonicalServer = $oldCanServer;
+ }
+
+ /**
+ * Provider of URL examples for testing wfExpandUrl()
+ */
+ public function provideExpandableUrls() {
+ $modes = array( 'http', 'https' );
+ $servers = array( 'http' => 'http://example.com', 'https' => 'https://example.com', 'protocol-relative' => '//example.com' );
+ $defaultProtos = array( 'http' => PROTO_HTTP, 'https' => PROTO_HTTPS, 'protocol-relative' => PROTO_RELATIVE, 'current' => PROTO_CURRENT, 'canonical' => PROTO_CANONICAL );
+
+ $retval = array();
+ foreach ( $modes as $mode ) {
+ $httpsMode = $mode == 'https';
+ foreach ( $servers as $serverDesc => $server ) {
+ foreach ( $modes as $canServerMode ) {
+ $canServer = "$canServerMode://example2.com";
+ foreach ( $defaultProtos as $protoDesc => $defaultProto ) {
+ $retval[] = array( 'http://example.com', 'http://example.com', $defaultProto, $server, $canServer, $httpsMode, "Testing fully qualified http URLs (no need to expand) (defaultProto: $protoDesc , wgServer: $server, wgCanonicalServer: $canServer, current request protocol: $mode )" );
+ $retval[] = array( 'https://example.com', 'https://example.com', $defaultProto, $server, $canServer, $httpsMode, "Testing fully qualified https URLs (no need to expand) (defaultProto: $protoDesc , wgServer: $server, wgCanonicalServer: $canServer, current request protocol: $mode )" );
+ # Would be nice to support this, see fixme on wfExpandUrl()
+ $retval[] = array( "wiki/FooBar", 'wiki/FooBar', $defaultProto, $server, $canServer, $httpsMode, "Test non-expandable relative URLs (defaultProto: $protoDesc , wgServer: $server, wgCanonicalServer: $canServer, current request protocol: $mode )" );
+
+ // Determine expected protocol
+ $p = $protoDesc . ':'; // default case
+ if ( $protoDesc == 'protocol-relative' ) {
+ $p = '';
+ } else if ( $protoDesc == 'current' ) {
+ $p = "$mode:";
+ } else if ( $protoDesc == 'canonical' ) {
+ $p = "$canServerMode:";
+ } else {
+ $p = $protoDesc . ':';
+ }
+ // Determine expected server name
+ if ( $protoDesc == 'canonical' ) {
+ $srv = $canServer;
+ } else if ( $serverDesc == 'protocol-relative' ) {
+ $srv = $p . $server;
+ } else {
+ $srv = $server;
+ }
+
+ $retval[] = array( "$p//wikipedia.org", '//wikipedia.org', $defaultProto, $server, $canServer, $httpsMode, "Test protocol-relative URL (defaultProto: $protoDesc, wgServer: $server, wgCanonicalServer: $canServer, current request protocol: $mode )" );
+ $retval[] = array( "$srv/wiki/FooBar", '/wiki/FooBar', $defaultProto, $server, $canServer, $httpsMode, "Testing expanding URL beginning with / (defaultProto: $protoDesc , wgServer: $server, wgCanonicalServer: $canServer, current request protocol: $mode )" );
+ }
+ }
+ }
+ }
+ return $retval;
+ }
+}
diff --git a/tests/phpunit/includes/GlobalFunctions/wfUrlencodeTest.php b/tests/phpunit/includes/GlobalFunctions/wfUrlencodeTest.php
new file mode 100644
index 00000000..cd1a8dbd
--- /dev/null
+++ b/tests/phpunit/includes/GlobalFunctions/wfUrlencodeTest.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * Tests for includes/GlobalFunctions.php -> wfUrlencode()
+ *
+ * The function only need a string parameter and might react to IIS7.0
+ */
+
+class wfUrlencodeTest extends MediaWikiTestCase {
+
+ #### TESTS ##############################################################
+
+ /** @dataProvider provideURLS */
+ public function testEncodingUrlWith( $input, $expected ) {
+ $this->verifyEncodingFor( 'Apache', $input, $expected );
+ }
+
+ /** @dataProvider provideURLS */
+ public function testEncodingUrlWithMicrosoftIis7( $input, $expected ) {
+ $this->verifyEncodingFor( 'Microsoft-IIS/7', $input, $expected );
+ }
+
+ #### HELPERS #############################################################
+
+ /**
+ * Internal helper that actually run the test.
+ * Called by the public methods testEncodingUrlWith...()
+ *
+ */
+ private function verifyEncodingFor( $server, $input, $expectations ) {
+ $expected = $this->extractExpect( $server, $expectations );
+
+ // save up global
+ $old = isset($_SERVER['SERVER_SOFTWARE'])
+ ? $_SERVER['SERVER_SOFTWARE']
+ : null
+ ;
+ $_SERVER['SERVER_SOFTWARE'] = $server;
+ wfUrlencode( null );
+
+ // do the requested test
+ $this->assertEquals(
+ $expected,
+ wfUrlencode( $input ),
+ "Encoding '$input' for server '$server' should be '$expected'"
+ );
+
+ // restore global
+ if( $old === null ) {
+ unset( $_SERVER['SERVER_SOFTWARE'] );
+ } else {
+ $_SERVER['SERVER_SOFTWARE'] = $old;
+ }
+ wfUrlencode( null );
+ }
+
+ /**
+ * Interprets the provider array. Return expected value depending
+ * the HTTP server name.
+ */
+ private function extractExpect( $server, $expectations ) {
+ if( is_string( $expectations ) ) {
+ return $expectations;
+ } elseif( is_array( $expectations ) ) {
+ if( !array_key_exists( $server, $expectations ) ) {
+ throw new MWException( __METHOD__ . " expectation does not have any value for server name $server. Check the provider array.\n" );
+ } else {
+ return $expectations[$server];
+ }
+ } else {
+ throw new MWException( __METHOD__ . " given invalid expectation for '$server'. Should be a string or an array( <http server name> => <string> ).\n" );
+ }
+ }
+
+
+ #### PROVIDERS ###########################################################
+
+ /**
+ * Format is either:
+ * array( 'input', 'expected' );
+ * Or:
+ * array( 'input',
+ * array( 'Apache', 'expected' ),
+ * array( 'Microsoft-IIS/7', 'expected' ),
+ * ),
+ * If you want to add other HTTP server name, you will have to add a new
+ * testing method much like the testEncodingUrlWith() method above.
+ */
+ public function provideURLS() {
+ return array(
+ ### RFC 1738 chars
+ // + is not safe
+ array( '+', '%2B' ),
+ // & and = not safe in queries
+ array( '&', '%26' ),
+ array( '=', '%3D' ),
+
+ array( ':', array(
+ 'Apache' => ':',
+ 'Microsoft-IIS/7' => '%3A',
+ ) ),
+
+ // remaining chars do not need encoding
+ array(
+ ';@$-_.!*',
+ ';@$-_.!*',
+ ),
+
+ ### Other tests
+ // slash remain unchanged. %2F seems to break things
+ array( '/', '/' ),
+
+ // Other 'funnies' chars
+ array( '[]', '%5B%5D' ),
+ array( '<>', '%3C%3E' ),
+
+ // Apostrophe is encoded
+ array( '\'', '%27' ),
+ );
+ }
+}
diff --git a/tests/phpunit/includes/HooksTest.php b/tests/phpunit/includes/HooksTest.php
new file mode 100644
index 00000000..2f9d9f8d
--- /dev/null
+++ b/tests/phpunit/includes/HooksTest.php
@@ -0,0 +1,102 @@
+<?php
+
+class HooksTest extends MediaWikiTestCase {
+
+ public function testOldStyleHooks() {
+ $foo = 'Foo';
+ $bar = 'Bar';
+
+ $i = new NothingClass();
+
+ global $wgHooks;
+
+ $wgHooks['MediaWikiHooksTest001'][] = array( $i, 'someNonStatic' );
+
+ wfRunHooks( 'MediaWikiHooksTest001', array( &$foo, &$bar ) );
+
+ $this->assertEquals( 'fOO', $foo, 'Standard method' );
+ $foo = 'Foo';
+
+ $wgHooks['MediaWikiHooksTest001'][] = $i;
+
+ wfRunHooks( 'MediaWikiHooksTest001', array( &$foo, &$bar ) );
+
+ $this->assertEquals( 'foo', $foo, 'onEventName style' );
+ $foo = 'Foo';
+
+ $wgHooks['MediaWikiHooksTest001'][] = array( $i, 'someNonStaticWithData', 'baz' );
+
+ wfRunHooks( 'MediaWikiHooksTest001', array( &$foo, &$bar ) );
+
+ $this->assertEquals( 'baz', $foo, 'Data included' );
+ $foo = 'Foo';
+
+ $wgHooks['MediaWikiHooksTest001'][] = array( $i, 'someStatic' );
+
+ wfRunHooks( 'MediaWikiHooksTest001', array( &$foo, &$bar ) );
+
+ $this->assertEquals( 'bah', $foo, 'Standard static method' );
+ //$foo = 'Foo';
+
+ unset( $wgHooks['MediaWikiHooksTest001'] );
+
+ }
+
+ public function testNewStyleHooks() {
+ $foo = 'Foo';
+ $bar = 'Bar';
+
+ $i = new NothingClass();
+
+ Hooks::register( 'MediaWikiHooksTest001', array( $i, 'someNonStatic' ) );
+
+ Hooks::run( 'MediaWikiHooksTest001', array( &$foo, &$bar ) );
+
+ $this->assertEquals( 'fOO', $foo, 'Standard method' );
+ $foo = 'Foo';
+
+ Hooks::register( 'MediaWikiHooksTest001', $i );
+
+ Hooks::run( 'MediaWikiHooksTest001', array( &$foo, &$bar ) );
+
+ $this->assertEquals( 'foo', $foo, 'onEventName style' );
+ $foo = 'Foo';
+
+ Hooks::register( 'MediaWikiHooksTest001', array( $i, 'someNonStaticWithData', 'baz' ) );
+
+ Hooks::run( 'MediaWikiHooksTest001', array( &$foo, &$bar ) );
+
+ $this->assertEquals( 'baz', $foo, 'Data included' );
+ $foo = 'Foo';
+
+ Hooks::register( 'MediaWikiHooksTest001', array( $i, 'someStatic' ) );
+
+ Hooks::run( 'MediaWikiHooksTest001', array( &$foo, &$bar ) );
+
+ $this->assertEquals( 'bah', $foo, 'Standard static method' );
+ $foo = 'Foo';
+ }
+}
+
+class NothingClass {
+ static public function someStatic( &$foo, &$bar ) {
+ $foo = 'bah';
+ return true;
+ }
+
+ public function someNonStatic( &$foo, &$bar ) {
+ $foo = 'fOO';
+ $bar = 'bAR';
+ return true;
+ }
+
+ public function onMediaWikiHooksTest001( &$foo, &$bar ) {
+ $foo = 'foo';
+ return true;
+ }
+
+ public function someNonStaticWithData( $foo, &$bar ) {
+ $bar = $foo;
+ return true;
+ }
+}
diff --git a/tests/phpunit/includes/HtmlTest.php b/tests/phpunit/includes/HtmlTest.php
new file mode 100644
index 00000000..96bb1803
--- /dev/null
+++ b/tests/phpunit/includes/HtmlTest.php
@@ -0,0 +1,90 @@
+<?php
+/** tests for includes/Html.php */
+
+class HtmlTest extends MediaWikiTestCase {
+ private static $oldLang;
+
+ public function setUp() {
+ global $wgLang, $wgLanguageCode;
+
+ self::$oldLang = $wgLang;
+ $wgLanguageCode = 'en';
+ $wgLang = Language::factory( $wgLanguageCode );
+ }
+
+ public function tearDown() {
+ global $wgLang, $wgLanguageCode;
+ $wgLang = self::$oldLang;
+ $wgLanguageCode = $wgLang->getCode();
+ }
+
+ public function testExpandAttributesSkipsNullAndFalse() {
+
+ ### EMPTY ########
+ $this->AssertEmpty(
+ Html::expandAttributes( array( 'foo'=>null) ),
+ 'skip keys with null value'
+ );
+ $this->AssertEmpty(
+ Html::expandAttributes( array( 'foo'=>false) ),
+ 'skip keys with false value'
+ );
+ $this->AssertNotEmpty(
+ Html::expandAttributes( array( 'foo'=>'') ),
+ 'keep keys with an empty string'
+ );
+ }
+
+ public function testExpandAttributesForBooleans() {
+ $this->AssertEquals(
+ '',
+ Html::expandAttributes( array( 'selected'=>false) ),
+ 'Boolean attributes do not generates output when value is false'
+ );
+ $this->AssertEquals(
+ '',
+ Html::expandAttributes( array( 'selected'=>null) ),
+ 'Boolean attributes do not generates output when value is null'
+ );
+
+ ### FIXME: maybe they should just output 'selected'
+ $this->AssertEquals(
+ ' selected=""',
+ Html::expandAttributes( array( 'selected'=>true ) ),
+ 'Boolean attributes skip value output'
+ );
+ $this->AssertEquals(
+ ' selected=""',
+ Html::expandAttributes( array( 'selected' ) ),
+ 'Boolean attributes (ex: selected) do not need a value'
+ );
+ }
+
+ /**
+ * Test for Html::expandAttributes()
+ * Please note it output a string prefixed with a space!
+ */
+ public function testExpandAttributesVariousExpansions() {
+ ### NOT EMPTY ####
+ $this->AssertEquals(
+ ' empty_string=""',
+ Html::expandAttributes( array( 'empty_string'=>'') ),
+ 'Value with an empty string'
+ );
+ $this->AssertEquals(
+ ' key="value"',
+ Html::expandAttributes( array( 'key'=>'value') ),
+ 'Value is a string'
+ );
+ $this->AssertEquals(
+ ' one="1"',
+ Html::expandAttributes( array( 'one'=>1) ),
+ 'Value is a numeric one'
+ );
+ $this->AssertEquals(
+ ' zero="0"',
+ Html::expandAttributes( array( 'zero'=>0) ),
+ 'Value is a numeric zero'
+ );
+ }
+}
diff --git a/tests/phpunit/includes/HttpTest.php b/tests/phpunit/includes/HttpTest.php
new file mode 100644
index 00000000..1a99af7d
--- /dev/null
+++ b/tests/phpunit/includes/HttpTest.php
@@ -0,0 +1,618 @@
+<?php
+
+class MockCookie extends Cookie {
+ public function canServeDomain( $arg ) { return parent::canServeDomain( $arg ); }
+ public function canServePath( $arg ) { return parent::canServePath( $arg ); }
+ public function isUnExpired() { return parent::isUnExpired(); }
+}
+
+/**
+ * @group Broken
+ */
+class HttpTest extends MediaWikiTestCase {
+ static $content;
+ static $headers;
+ static $has_curl;
+ static $has_fopen;
+ static $has_proxy = false;
+ static $proxy = "http://hulk:8080/";
+ var $test_geturl = array(
+ "http://en.wikipedia.org/robots.txt",
+ "https://secure.wikimedia.org/",
+ "http://pecl.php.net/feeds/pkg_apc.rss",
+ "http://meta.wikimedia.org/w/index.php?title=Interwiki_map&action=raw",
+ "http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:MediaWiki_hooks&format=php",
+ );
+ var $test_requesturl = array( "http://en.wikipedia.org/wiki/Special:Export/User:MarkAHershberger" );
+
+ var $test_posturl = array( "http://www.comp.leeds.ac.uk/cgi-bin/Perl/environment-example" => "review=test" );
+
+ function setUp() {
+ putenv( "http_proxy" ); /* Remove any proxy env var, so curl doesn't get confused */
+ if ( is_array( self::$content ) ) {
+ return;
+ }
+ self::$has_curl = function_exists( 'curl_init' );
+ self::$has_fopen = wfIniGetBool( 'allow_url_fopen' );
+
+ if ( !file_exists( "/usr/bin/curl" ) ) {
+ $this->markTestIncomplete( "This test requires the curl binary at /usr/bin/curl. If you have curl, please file a bug on this test, or, better yet, provide a patch." );
+ }
+
+ $content = tempnam( wfTempDir(), "" );
+ $headers = tempnam( wfTempDir(), "" );
+ if ( !$content && !$headers ) {
+ die( "Couldn't create temp file!" );
+ }
+
+ // This probably isn't the best test for a proxy, but it works on my system!
+ system( "curl -0 -o $content -s " . self::$proxy );
+ $out = file_get_contents( $content );
+ if ( $out ) {
+ self::$has_proxy = true;
+ }
+
+ /* Maybe use wget instead of curl here ... just to use a different codebase? */
+ foreach ( $this->test_geturl as $u ) {
+ system( "curl -0 -s -D $headers '$u' -o $content" );
+ self::$content["GET $u"] = file_get_contents( $content );
+ self::$headers["GET $u"] = file_get_contents( $headers );
+ }
+ foreach ( $this->test_requesturl as $u ) {
+ system( "curl -0 -s -X POST -H 'Content-Length: 0' -D $headers '$u' -o $content" );
+ self::$content["POST $u"] = file_get_contents( $content );
+ self::$headers["POST $u"] = file_get_contents( $headers );
+ }
+ foreach ( $this->test_posturl as $u => $postData ) {
+ system( "curl -0 -s -X POST -d '$postData' -D $headers '$u' -o $content" );
+ self::$content["POST $u => $postData"] = file_get_contents( $content );
+ self::$headers["POST $u => $postData"] = file_get_contents( $headers );
+ }
+ unlink( $content );
+ unlink( $headers );
+ }
+
+
+ function testInstantiation() {
+ Http::$httpEngine = false;
+
+ $r = MWHttpRequest::factory( "http://www.example.com/" );
+ if ( self::$has_curl ) {
+ $this->assertThat( $r, $this->isInstanceOf( 'CurlHttpRequest' ) );
+ } else {
+ $this->assertThat( $r, $this->isInstanceOf( 'PhpHttpRequest' ) );
+ }
+ unset( $r );
+
+ if ( !self::$has_fopen ) {
+ $this->setExpectedException( 'MWException' );
+ }
+ Http::$httpEngine = 'php';
+ $r = MWHttpRequest::factory( "http://www.example.com/" );
+ $this->assertThat( $r, $this->isInstanceOf( 'PhpHttpRequest' ) );
+ unset( $r );
+
+ if ( !self::$has_curl ) {
+ $this->setExpectedException( 'MWException' );
+ }
+ Http::$httpEngine = 'curl';
+ $r = MWHttpRequest::factory( "http://www.example.com/" );
+ if ( self::$has_curl ) {
+ $this->assertThat( $r, $this->isInstanceOf( 'CurlHttpRequest' ) );
+ }
+ }
+
+ function runHTTPFailureChecks() {
+ // Each of the following requests should result in a failure.
+
+ $timeout = 1;
+ $start_time = time();
+ $r = Http::get( "http://www.example.com:1/", $timeout );
+ $end_time = time();
+ $this->assertLessThan( $timeout + 2, $end_time - $start_time,
+ "Request took less than {$timeout}s via " . Http::$httpEngine );
+ $this->assertEquals( $r, false, "false -- what we get on error from Http::get()" );
+
+ $r = Http::get( "http://www.mediawiki.org/xml/made-up-url", $timeout );
+ $this->assertFalse( $r, "False on 404s" );
+
+
+ $r = MWHttpRequest::factory( "http://www.mediawiki.org/xml/made-up-url" );
+ $er = $r->execute();
+ if ( $r instanceof PhpHttpRequest && version_compare( '5.2.10', phpversion(), '>' ) ) {
+ $this->assertRegexp( "/HTTP request failed/", $er->getWikiText() );
+ } else {
+ $this->assertRegexp( "/404 Not Found/", $er->getWikiText() );
+ }
+ }
+
+ function testFailureDefault() {
+ Http::$httpEngine = false;
+ $this->runHTTPFailureChecks();
+ }
+
+ function testFailurePhp() {
+ if ( !self::$has_fopen ) {
+ $this->markTestIncomplete( "This test requires allow_url_fopen=true." );
+ }
+
+ Http::$httpEngine = "php";
+ $this->runHTTPFailureChecks();
+ }
+
+ function testFailureCurl() {
+ if ( !self::$has_curl ) {
+ $this->markTestIncomplete( "This test requires curl." );
+ }
+
+ Http::$httpEngine = "curl";
+ $this->runHTTPFailureChecks();
+ }
+
+ /* ./phase3/includes/Import.php:1108: $data = Http::request( $method, $url ); */
+ /* ./includes/Import.php:1124: $link = Title::newFromText( "$interwiki:Special:Export/$page" ); */
+ /* ./includes/Import.php:1134: return ImportStreamSource::newFromURL( $url, "POST" ); */
+ function runHTTPRequests( $proxy = null ) {
+ $opt = array();
+
+ if ( $proxy ) {
+ $opt['proxy'] = $proxy;
+ } elseif ( $proxy === false ) {
+ $opt['noProxy'] = true;
+ }
+
+ /* no postData here because the only request I could find in code so far didn't have any */
+ foreach ( $this->test_requesturl as $u ) {
+ $r = Http::request( "POST", $u, $opt );
+ $this->assertEquals( self::$content["POST $u"], "$r", "POST $u with " . Http::$httpEngine );
+ }
+ }
+
+ function testRequestDefault() {
+ Http::$httpEngine = false;
+ $this->runHTTPRequests();
+ }
+
+ function testRequestPhp() {
+ if ( !self::$has_fopen ) {
+ $this->markTestIncomplete( "This test requires allow_url_fopen=true." );
+ }
+
+ Http::$httpEngine = "php";
+ $this->runHTTPRequests();
+ }
+
+ function testRequestCurl() {
+ if ( !self::$has_curl ) {
+ $this->markTestIncomplete( "This test requires curl." );
+ }
+
+ Http::$httpEngine = "curl";
+ $this->runHTTPRequests();
+ }
+
+ function runHTTPGets( $proxy = null ) {
+ $opt = array();
+
+ if ( $proxy ) {
+ $opt['proxy'] = $proxy;
+ } elseif ( $proxy === false ) {
+ $opt['noProxy'] = true;
+ }
+
+ foreach ( $this->test_geturl as $u ) {
+ $r = Http::get( $u, 30, $opt ); /* timeout of 30s */
+ $this->assertEquals( self::$content["GET $u"], "$r", "Get $u with " . Http::$httpEngine );
+ }
+ }
+
+ function testGetDefault() {
+ Http::$httpEngine = false;
+ $this->runHTTPGets();
+ }
+
+ function testGetPhp() {
+ if ( !self::$has_fopen ) {
+ $this->markTestIncomplete( "This test requires allow_url_fopen=true." );
+ }
+
+ Http::$httpEngine = "php";
+ $this->runHTTPGets();
+ }
+
+ function testGetCurl() {
+ if ( !self::$has_curl ) {
+ $this->markTestIncomplete( "This test requires curl." );
+ }
+
+ Http::$httpEngine = "curl";
+ $this->runHTTPGets();
+ }
+
+ function runHTTPPosts( $proxy = null ) {
+ $opt = array();
+
+ if ( $proxy ) {
+ $opt['proxy'] = $proxy;
+ } elseif ( $proxy === false ) {
+ $opt['noProxy'] = true;
+ }
+
+ foreach ( $this->test_posturl as $u => $postData ) {
+ $opt['postData'] = $postData;
+ $r = Http::post( $u, $opt );
+ $this->assertEquals( self::$content["POST $u => $postData"], "$r",
+ "POST $u (postData=$postData) with " . Http::$httpEngine );
+ }
+ }
+
+ function testPostDefault() {
+ Http::$httpEngine = false;
+ $this->runHTTPPosts();
+ }
+
+ function testPostPhp() {
+ if ( !self::$has_fopen ) {
+ $this->markTestIncomplete( "This test requires allow_url_fopen=true." );
+ }
+
+ Http::$httpEngine = "php";
+ $this->runHTTPPosts();
+ }
+
+ function testPostCurl() {
+ if ( !self::$has_curl ) {
+ $this->markTestIncomplete( "This test requires curl." );
+ }
+
+ Http::$httpEngine = "curl";
+ $this->runHTTPPosts();
+ }
+
+ function runProxyRequests() {
+ if ( !self::$has_proxy ) {
+ $this->markTestIncomplete( "This test requires a proxy." );
+ }
+ $this->runHTTPGets( self::$proxy );
+ $this->runHTTPPosts( self::$proxy );
+ $this->runHTTPRequests( self::$proxy );
+
+ // Set false here to do noProxy
+ $this->runHTTPGets( false );
+ $this->runHTTPPosts( false );
+ $this->runHTTPRequests( false );
+ }
+
+ function testProxyDefault() {
+ Http::$httpEngine = false;
+ $this->runProxyRequests();
+ }
+
+ function testProxyPhp() {
+ if ( !self::$has_fopen ) {
+ $this->markTestIncomplete( "This test requires allow_url_fopen=true." );
+ }
+
+ Http::$httpEngine = 'php';
+ $this->runProxyRequests();
+ }
+
+ function testProxyCurl() {
+ if ( !self::$has_curl ) {
+ $this->markTestIncomplete( "This test requires curl." );
+ }
+
+ Http::$httpEngine = 'curl';
+ $this->runProxyRequests();
+ }
+
+ function testIsLocalUrl() {
+ }
+
+ /* ./extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.body.php:559: $user_agent = Http::userAgent(); */
+ function testUserAgent() {
+ }
+
+ function testIsValidUrl() {
+ }
+
+ /**
+ * @dataProvider cookieDomains
+ */
+ function testValidateCookieDomain( $expected, $domain, $origin=null ) {
+ if ( $origin ) {
+ $ok = Cookie::validateCookieDomain( $domain, $origin );
+ $msg = "$domain against origin $origin";
+ } else {
+ $ok = Cookie::validateCookieDomain( $domain );
+ $msg = "$domain";
+ }
+ $this->assertEquals( $expected, $ok, $msg );
+ }
+
+ function cookieDomains() {
+ return array(
+ array( false, "org"),
+ array( false, ".org"),
+ array( true, "wikipedia.org"),
+ array( true, ".wikipedia.org"),
+ array( false, "co.uk" ),
+ array( false, ".co.uk" ),
+ array( false, "gov.uk" ),
+ array( false, ".gov.uk" ),
+ array( true, "supermarket.uk" ),
+ array( false, "uk" ),
+ array( false, ".uk" ),
+ array( false, "127.0.0." ),
+ array( false, "127." ),
+ array( false, "127.0.0.1." ),
+ array( true, "127.0.0.1" ),
+ array( false, "333.0.0.1" ),
+ array( true, "example.com" ),
+ array( false, "example.com." ),
+ array( true, ".example.com" ),
+
+ array( true, ".example.com", "www.example.com" ),
+ array( false, "example.com", "www.example.com" ),
+ array( true, "127.0.0.1", "127.0.0.1" ),
+ array( false, "127.0.0.1", "localhost" ),
+ );
+ }
+
+ function testSetCooke() {
+ $c = new MockCookie( "name", "value",
+ array(
+ "domain" => "ac.th",
+ "path" => "/path/",
+ ) );
+ $this->assertFalse( $c->canServeDomain( "ac.th" ) );
+
+ $c = new MockCookie( "name", "value",
+ array(
+ "domain" => "example.com",
+ "path" => "/path/",
+ ) );
+
+ $this->assertTrue( $c->canServeDomain( "example.com" ) );
+ $this->assertFalse( $c->canServeDomain( "www.example.com" ) );
+
+ $c = new MockCookie( "name", "value",
+ array(
+ "domain" => ".example.com",
+ "path" => "/path/",
+ ) );
+
+ $this->assertFalse( $c->canServeDomain( "www.example.net" ) );
+ $this->assertFalse( $c->canServeDomain( "example.com" ) );
+ $this->assertTrue( $c->canServeDomain( "www.example.com" ) );
+
+ $this->assertFalse( $c->canServePath( "/" ) );
+ $this->assertFalse( $c->canServePath( "/bogus/path/" ) );
+ $this->assertFalse( $c->canServePath( "/path" ) );
+ $this->assertTrue( $c->canServePath( "/path/" ) );
+
+ $this->assertTrue( $c->isUnExpired() );
+
+ $this->assertEquals( "", $c->serializeToHttpRequest( "/path/", "www.example.net" ) );
+ $this->assertEquals( "", $c->serializeToHttpRequest( "/", "www.example.com" ) );
+ $this->assertEquals( "name=value", $c->serializeToHttpRequest( "/path/", "www.example.com" ) );
+
+ $c = new MockCookie( "name", "value",
+ array(
+ "domain" => "www.example.com",
+ "path" => "/path/",
+ ) );
+ $this->assertFalse( $c->canServeDomain( "example.com" ) );
+ $this->assertFalse( $c->canServeDomain( "www.example.net" ) );
+ $this->assertTrue( $c->canServeDomain( "www.example.com" ) );
+
+ $c = new MockCookie( "name", "value",
+ array(
+ "domain" => ".example.com",
+ "path" => "/path/",
+ "expires" => "-1 day",
+ ) );
+ $this->assertFalse( $c->isUnExpired() );
+ $this->assertEquals( "", $c->serializeToHttpRequest( "/path/", "www.example.com" ) );
+
+ $c = new MockCookie( "name", "value",
+ array(
+ "domain" => ".example.com",
+ "path" => "/path/",
+ "expires" => "+1 day",
+ ) );
+ $this->assertTrue( $c->isUnExpired() );
+ $this->assertEquals( "name=value", $c->serializeToHttpRequest( "/path/", "www.example.com" ) );
+ }
+
+ function testCookieJarSetCookie() {
+ $cj = new CookieJar;
+ $cj->setCookie( "name", "value",
+ array(
+ "domain" => ".example.com",
+ "path" => "/path/",
+ ) );
+ $cj->setCookie( "name2", "value",
+ array(
+ "domain" => ".example.com",
+ "path" => "/path/sub",
+ ) );
+ $cj->setCookie( "name3", "value",
+ array(
+ "domain" => ".example.com",
+ "path" => "/",
+ ) );
+ $cj->setCookie( "name4", "value",
+ array(
+ "domain" => ".example.net",
+ "path" => "/path/",
+ ) );
+ $cj->setCookie( "name5", "value",
+ array(
+ "domain" => ".example.net",
+ "path" => "/path/",
+ "expires" => "-1 day",
+ ) );
+
+ $this->assertEquals( "name4=value", $cj->serializeToHttpRequest( "/path/", "www.example.net" ) );
+ $this->assertEquals( "name3=value", $cj->serializeToHttpRequest( "/", "www.example.com" ) );
+ $this->assertEquals( "name=value; name3=value", $cj->serializeToHttpRequest( "/path/", "www.example.com" ) );
+
+ $cj->setCookie( "name5", "value",
+ array(
+ "domain" => ".example.net",
+ "path" => "/path/",
+ "expires" => "+1 day",
+ ) );
+ $this->assertEquals( "name4=value; name5=value", $cj->serializeToHttpRequest( "/path/", "www.example.net" ) );
+
+ $cj->setCookie( "name4", "value",
+ array(
+ "domain" => ".example.net",
+ "path" => "/path/",
+ "expires" => "-1 day",
+ ) );
+ $this->assertEquals( "name5=value", $cj->serializeToHttpRequest( "/path/", "www.example.net" ) );
+ }
+
+ function testParseResponseHeader() {
+ $cj = new CookieJar;
+
+ $h[] = "Set-Cookie: name4=value; domain=.example.com; path=/; expires=Mon, 09-Dec-2029 13:46:00 GMT";
+ $cj->parseCookieResponseHeader( $h[0], "www.example.com" );
+ $this->assertEquals( "name4=value", $cj->serializeToHttpRequest( "/", "www.example.com" ) );
+
+ $h[] = "name4=value2; domain=.example.com; path=/path/; expires=Mon, 09-Dec-2029 13:46:00 GMT";
+ $cj->parseCookieResponseHeader( $h[1], "www.example.com" );
+ $this->assertEquals( "", $cj->serializeToHttpRequest( "/", "www.example.com" ) );
+ $this->assertEquals( "name4=value2", $cj->serializeToHttpRequest( "/path/", "www.example.com" ) );
+
+ $h[] = "name5=value3; domain=.example.com; path=/path/; expires=Mon, 09-Dec-2029 13:46:00 GMT";
+ $cj->parseCookieResponseHeader( $h[2], "www.example.com" );
+ $this->assertEquals( "name4=value2; name5=value3", $cj->serializeToHttpRequest( "/path/", "www.example.com" ) );
+
+ $h[] = "name6=value3; domain=.example.net; path=/path/; expires=Mon, 09-Dec-2029 13:46:00 GMT";
+ $cj->parseCookieResponseHeader( $h[3], "www.example.com" );
+ $this->assertEquals( "", $cj->serializeToHttpRequest( "/path/", "www.example.net" ) );
+
+ $h[] = "name6=value0; domain=.example.net; path=/path/; expires=Mon, 09-Dec-1999 13:46:00 GMT";
+ $cj->parseCookieResponseHeader( $h[4], "www.example.net" );
+ $this->assertEquals( "", $cj->serializeToHttpRequest( "/path/", "www.example.net" ) );
+
+ $h[] = "name6=value4; domain=.example.net; path=/path/; expires=Mon, 09-Dec-2029 13:46:00 GMT";
+ $cj->parseCookieResponseHeader( $h[5], "www.example.net" );
+ $this->assertEquals( "name6=value4", $cj->serializeToHttpRequest( "/path/", "www.example.net" ) );
+ }
+
+ function runCookieRequests() {
+ $r = MWHttpRequest::factory( "http://www.php.net/manual", array( 'followRedirects' => true ) );
+ $r->execute();
+
+ $jar = $r->getCookieJar();
+ $this->assertThat( $jar, $this->isInstanceOf( 'CookieJar' ) );
+
+ $serialized = $jar->serializeToHttpRequest( "/search?q=test", "www.php.net" );
+ $this->assertRegExp( '/\bCOUNTRY=[^=;]+/', $serialized );
+ $this->assertRegExp( '/\bLAST_LANG=[^=;]+/', $serialized );
+ $this->assertEquals( '', $jar->serializeToHttpRequest( "/search?q=test", "www.php.com" ) );
+ }
+
+ function testCookieRequestDefault() {
+ Http::$httpEngine = false;
+ $this->runCookieRequests();
+ }
+ function testCookieRequestPhp() {
+ if ( !self::$has_fopen ) {
+ $this->markTestIncomplete( "This test requires allow_url_fopen=true." );
+ }
+
+ Http::$httpEngine = 'php';
+ $this->runCookieRequests();
+ }
+ function testCookieRequestCurl() {
+ if ( !self::$has_curl ) {
+ $this->markTestIncomplete( "This test requires curl." );
+ }
+
+ Http::$httpEngine = 'curl';
+ $this->runCookieRequests();
+ }
+
+ /**
+ * Test Http::isValidURI()
+ * @bug 27854 : Http::isValidURI is to lax
+ *@dataProvider provideURI */
+ function testIsValidUri( $expect, $URI, $message = '' ) {
+ $this->assertEquals(
+ $expect,
+ (bool) Http::isValidURI( $URI ),
+ $message
+ );
+ }
+
+ /**
+ * Feeds URI to test a long regular expression in Http::isValidURI
+ */
+ function provideURI() {
+ /** Format: 'boolean expectation', 'URI to test', 'Optional message' */
+ return array(
+ array( false, '¿non sens before!! http://a', 'Allow anything before URI' ),
+
+ # (http|https) - only two schemes allowed
+ array( true, 'http://www.example.org/' ),
+ array( true, 'https://www.example.org/' ),
+ array( true, 'http://www.example.org', 'URI without directory' ),
+ array( true, 'http://a', 'Short name' ),
+ array( true, 'http://étoile', 'Allow UTF-8 in hostname' ), # 'étoile' is french for 'star'
+ array( false, '\\host\directory', 'CIFS share' ),
+ array( false, 'gopher://host/dir', 'Reject gopher scheme' ),
+ array( false, 'telnet://host', 'Reject telnet scheme' ),
+
+ # :\/\/ - double slashes
+ array( false, 'http//example.org', 'Reject missing colon in protocol' ),
+ array( false, 'http:/example.org', 'Reject missing slash in protocol' ),
+ array( false, 'http:example.org', 'Must have two slashes' ),
+ # Following fail since hostname can be made of anything
+ array( false, 'http:///example.org', 'Must have exactly two slashes, not three' ),
+
+ # (\w+:{0,1}\w*@)? - optional user:pass
+ array( true, 'http://user@host', 'Username provided' ),
+ array( true, 'http://user:@host', 'Username provided, no password' ),
+ array( true, 'http://user:pass@host', 'Username and password provided' ),
+
+ # (\S+) - host part is made of anything not whitespaces
+ array( false, 'http://!"èèè¿¿¿~~\'', 'hostname is made of any non whitespace' ),
+ array( false, 'http://exam:ple.org/', 'hostname can not use colons!' ),
+
+ # (:[0-9]+)? - port number
+ array( true, 'http://example.org:80/' ),
+ array( true, 'https://example.org:80/' ),
+ array( true, 'http://example.org:443/' ),
+ array( true, 'https://example.org:443/' ),
+
+ # Part after the hostname is / or / with something else
+ array( true, 'http://example/#' ),
+ array( true, 'http://example/!' ),
+ array( true, 'http://example/:' ),
+ array( true, 'http://example/.' ),
+ array( true, 'http://example/?' ),
+ array( true, 'http://example/+' ),
+ array( true, 'http://example/=' ),
+ array( true, 'http://example/&' ),
+ array( true, 'http://example/%' ),
+ array( true, 'http://example/@' ),
+ array( true, 'http://example/-' ),
+ array( true, 'http://example//' ),
+ array( true, 'http://example/&' ),
+
+ # Fragment
+ array( true, 'http://exam#ple.org', ), # This one is valid, really!
+ array( true, 'http://example.org:80#anchor' ),
+ array( true, 'http://example.org/?id#anchor' ),
+ array( true, 'http://example.org/?#anchor' ),
+
+ array( false, 'http://a ¿non !!sens after', 'Allow anything after URI' ),
+ );
+ }
+
+}
diff --git a/tests/phpunit/includes/IPTest.php b/tests/phpunit/includes/IPTest.php
new file mode 100644
index 00000000..c77dd852
--- /dev/null
+++ b/tests/phpunit/includes/IPTest.php
@@ -0,0 +1,508 @@
+<?php
+/*
+ * Tests for IP validity functions. Ported from /t/inc/IP.t by avar.
+ */
+
+class IPTest extends MediaWikiTestCase {
+ /**
+ * not sure it should be tested with boolean false. hashar 20100924
+ * @covers IP::isIPAddress
+ */
+ public function testisIPAddress() {
+ $this->assertFalse( IP::isIPAddress( false ), 'Boolean false is not an IP' );
+ $this->assertFalse( IP::isIPAddress( true ), 'Boolean true is not an IP' );
+ $this->assertFalse( IP::isIPAddress( "" ), 'Empty string is not an IP' );
+ $this->assertFalse( IP::isIPAddress( 'abc' ), 'Garbage IP string' );
+ $this->assertFalse( IP::isIPAddress( ':' ), 'Single ":" is not an IP' );
+ $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::1'), 'IPv6 with a double :: occurence' );
+ $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::'), 'IPv6 with a double :: occurence, last at end' );
+ $this->assertFalse( IP::isIPAddress( '::2001:0DB8::5:1'), 'IPv6 with a double :: occurence, firt at beginning' );
+ $this->assertFalse( IP::isIPAddress( '124.24.52' ), 'IPv4 not enough quads' );
+ $this->assertFalse( IP::isIPAddress( '24.324.52.13' ), 'IPv4 out of range' );
+ $this->assertFalse( IP::isIPAddress( '.24.52.13' ), 'IPv4 starts with period' );
+ $this->assertFalse( IP::isIPAddress( 'fc:100:300' ), 'IPv6 with only 3 words' );
+
+ $this->assertTrue( IP::isIPAddress( '::' ), 'RFC 4291 IPv6 Unspecified Address' );
+ $this->assertTrue( IP::isIPAddress( '::1' ), 'RFC 4291 IPv6 Loopback Address' );
+ $this->assertTrue( IP::isIPAddress( '74.24.52.13/20', 'IPv4 range' ) );
+ $this->assertTrue( IP::isIPAddress( 'fc:100:a:d:1:e:ac:0/24' ), 'IPv6 range' );
+ $this->assertTrue( IP::isIPAddress( 'fc::100:a:d:1:e:ac/96' ), 'IPv6 range with "::"' );
+
+ $validIPs = array( 'fc:100::', 'fc:100:a:d:1:e:ac::', 'fc::100', '::fc:100:a:d:1:e:ac',
+ '::fc', 'fc::100:a:d:1:e:ac', 'fc:100:a:d:1:e:ac:0', '124.24.52.13', '1.24.52.13' );
+ foreach ( $validIPs as $ip ) {
+ $this->assertTrue( IP::isIPAddress( $ip ), "$ip is a valid IP address" );
+ }
+ }
+
+ /**
+ * @covers IP::isIPv6
+ */
+ public function testisIPv6() {
+ $this->assertFalse( IP::isIPv6( ':fc:100::' ), 'IPv6 starting with lone ":"' );
+ $this->assertFalse( IP::isIPv6( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
+ $this->assertFalse( IP::isIPv6( 'fc:300' ), 'IPv6 with only 2 words' );
+ $this->assertFalse( IP::isIPv6( 'fc:100:300' ), 'IPv6 with only 3 words' );
+
+ $this->assertTrue( IP::isIPv6( 'fc:100::' ) );
+ $this->assertTrue( IP::isIPv6( 'fc:100:a::' ) );
+ $this->assertTrue( IP::isIPv6( 'fc:100:a:d::' ) );
+ $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1::' ) );
+ $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e::' ) );
+ $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac::' ) );
+
+ $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 with 8 words ending with "::"' );
+ $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0:1::' ), 'IPv6 with 9 words ending with "::"' );
+
+ $this->assertFalse( IP::isIPv6( ':::' ) );
+ $this->assertFalse( IP::isIPv6( '::0:' ), 'IPv6 ending in a lone ":"' );
+
+ $this->assertTrue( IP::isIPv6( '::' ), 'IPv6 zero address' );
+ $this->assertTrue( IP::isIPv6( '::0' ) );
+ $this->assertTrue( IP::isIPv6( '::fc' ) );
+ $this->assertTrue( IP::isIPv6( '::fc:100' ) );
+ $this->assertTrue( IP::isIPv6( '::fc:100:a' ) );
+ $this->assertTrue( IP::isIPv6( '::fc:100:a:d' ) );
+ $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1' ) );
+ $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e' ) );
+ $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e:ac' ) );
+
+ $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
+ $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
+
+ $this->assertFalse( IP::isIPv6( ':fc::100' ), 'IPv6 starting with lone ":"' );
+ $this->assertFalse( IP::isIPv6( 'fc::100:' ), 'IPv6 ending with lone ":"' );
+ $this->assertFalse( IP::isIPv6( 'fc:::100' ), 'IPv6 with ":::" in the middle' );
+
+ $this->assertTrue( IP::isIPv6( 'fc::100' ), 'IPv6 with "::" and 2 words' );
+ $this->assertTrue( IP::isIPv6( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
+ $this->assertTrue( IP::isIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' ) );
+ $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
+ $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e' ), 'IPv6 with "::" and 6 words' );
+ $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
+ $this->assertTrue( IP::isIPv6( '2001::df'), 'IPv6 with "::" and 2 words' );
+ $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df'), 'IPv6 with "::" and 5 words' );
+ $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df:2'), 'IPv6 with "::" and 6 words' );
+
+ $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
+ $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
+
+ $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac:0' ) );
+ }
+
+ /**
+ * @covers IP::isIPv4
+ */
+ public function testisIPv4() {
+ $this->assertFalse( IP::isIPv4( false ), 'Boolean false is not an IP' );
+ $this->assertFalse( IP::isIPv4( true ), 'Boolean true is not an IP' );
+ $this->assertFalse( IP::isIPv4( "" ), 'Empty string is not an IP' );
+ $this->assertFalse( IP::isIPv4( 'abc' ) );
+ $this->assertFalse( IP::isIPv4( ':' ) );
+ $this->assertFalse( IP::isIPv4( '124.24.52' ), 'IPv4 not enough quads' );
+ $this->assertFalse( IP::isIPv4( '24.324.52.13' ), 'IPv4 out of range' );
+ $this->assertFalse( IP::isIPv4( '.24.52.13' ), 'IPv4 starts with period' );
+
+ $this->assertTrue( IP::isIPv4( '124.24.52.13' ) );
+ $this->assertTrue( IP::isIPv4( '1.24.52.13' ) );
+ $this->assertTrue( IP::isIPv4( '74.24.52.13/20', 'IPv4 range' ) );
+ }
+
+ /**
+ * @covers IP::isValid
+ */
+ public function testValidIPs() {
+ foreach ( range( 0, 255 ) as $i ) {
+ $a = sprintf( "%03d", $i );
+ $b = sprintf( "%02d", $i );
+ $c = sprintf( "%01d", $i );
+ foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
+ $ip = "$f.$f.$f.$f";
+ $this->assertTrue( IP::isValid( $ip ) , "$ip is a valid IPv4 address" );
+ }
+ }
+ foreach ( range( 0x0, 0xFFFF, 0xF ) as $i ) {
+ $a = sprintf( "%04x", $i );
+ $b = sprintf( "%03x", $i );
+ $c = sprintf( "%02x", $i );
+ foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
+ $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
+ $this->assertTrue( IP::isValid( $ip ) , "$ip is a valid IPv6 address" );
+ }
+ }
+ // test with some abbreviations
+ $this->assertFalse( IP::isValid( ':fc:100::' ), 'IPv6 starting with lone ":"' );
+ $this->assertFalse( IP::isValid( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
+ $this->assertFalse( IP::isValid( 'fc:300' ), 'IPv6 with only 2 words' );
+ $this->assertFalse( IP::isValid( 'fc:100:300' ), 'IPv6 with only 3 words' );
+
+ $this->assertTrue( IP::isValid( 'fc:100::' ) );
+ $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e::' ) );
+ $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e:ac::' ) );
+
+ $this->assertTrue( IP::isValid( 'fc::100' ), 'IPv6 with "::" and 2 words' );
+ $this->assertTrue( IP::isValid( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
+ $this->assertTrue( IP::isValid( '2001::df'), 'IPv6 with "::" and 2 words' );
+ $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df'), 'IPv6 with "::" and 5 words' );
+ $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df:2'), 'IPv6 with "::" and 6 words' );
+ $this->assertTrue( IP::isValid( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
+ $this->assertTrue( IP::isValid( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
+
+ $this->assertFalse( IP::isValid( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 with 8 words ending with "::"' );
+ $this->assertFalse( IP::isValid( 'fc:100:a:d:1:e:ac:0:1::' ), 'IPv6 with 9 words ending with "::"' );
+ }
+
+ /**
+ * @covers IP::isValid
+ */
+ public function testInvalidIPs() {
+ // Out of range...
+ foreach ( range( 256, 999 ) as $i ) {
+ $a = sprintf( "%03d", $i );
+ $b = sprintf( "%02d", $i );
+ $c = sprintf( "%01d", $i );
+ foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
+ $ip = "$f.$f.$f.$f";
+ $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv4 address" );
+ }
+ }
+ foreach ( range( 'g', 'z' ) as $i ) {
+ $a = sprintf( "%04s", $i );
+ $b = sprintf( "%03s", $i );
+ $c = sprintf( "%02s", $i );
+ foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
+ $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
+ $this->assertFalse( IP::isValid( $ip ) , "$ip is not a valid IPv6 address" );
+ }
+ }
+ // Have CIDR
+ $ipCIDRs = array(
+ '212.35.31.121/32',
+ '212.35.31.121/18',
+ '212.35.31.121/24',
+ '::ff:d:321:5/96',
+ 'ff::d3:321:5/116',
+ 'c:ff:12:1:ea:d:321:5/120',
+ );
+ foreach ( $ipCIDRs as $i ) {
+ $this->assertFalse( IP::isValid( $i ),
+ "$i is an invalid IP address because it is a block" );
+ }
+ // Incomplete/garbage
+ $invalid = array(
+ 'www.xn--var-xla.net',
+ '216.17.184.G',
+ '216.17.184.1.',
+ '216.17.184',
+ '216.17.184.',
+ '256.17.184.1'
+ );
+ foreach ( $invalid as $i ) {
+ $this->assertFalse( IP::isValid( $i ), "$i is an invalid IP address" );
+ }
+ }
+
+ /**
+ * @covers IP::isValidBlock
+ */
+ public function testValidBlocks() {
+ $valid = array(
+ '116.17.184.5/32',
+ '0.17.184.5/30',
+ '16.17.184.1/24',
+ '30.242.52.14/1',
+ '10.232.52.13/8',
+ '30.242.52.14/0',
+ '::e:f:2001/96',
+ '::c:f:2001/128',
+ '::10:f:2001/70',
+ '::fe:f:2001/1',
+ '::6d:f:2001/8',
+ '::fe:f:2001/0',
+ );
+ foreach ( $valid as $i ) {
+ $this->assertTrue( IP::isValidBlock( $i ), "$i is a valid IP block" );
+ }
+ }
+
+ /**
+ * @covers IP::isValidBlock
+ */
+ public function testInvalidBlocks() {
+ $invalid = array(
+ '116.17.184.5/33',
+ '0.17.184.5/130',
+ '16.17.184.1/-1',
+ '10.232.52.13/*',
+ '7.232.52.13/ab',
+ '11.232.52.13/',
+ '::e:f:2001/129',
+ '::c:f:2001/228',
+ '::10:f:2001/-1',
+ '::6d:f:2001/*',
+ '::86:f:2001/ab',
+ '::23:f:2001/',
+ );
+ foreach ( $invalid as $i ) {
+ $this->assertFalse( IP::isValidBlock( $i ), "$i is not a valid IP block" );
+ }
+ }
+
+ /**
+ * Improve IP::sanitizeIP() code coverage
+ * @todo Most probably incomplete
+ */
+ public function testSanitizeIP() {
+ $this->assertNull( IP::sanitizeIP('') );
+ $this->assertNull( IP::sanitizeIP(' ') );
+ }
+
+ /**
+ * test wrapper around ip2long which might return -1 or false depending on PHP version
+ * @covers IP::toUnsigned
+ */
+ public function testip2longWrapper() {
+ // @todo FIXME: Add more tests ?
+ $this->assertEquals( pow(2,32) - 1, IP::toUnsigned( '255.255.255.255' ));
+ $i = 'IN.VA.LI.D';
+ $this->assertFalse( IP::toUnSigned( $i ) );
+ }
+
+ /**
+ * @covers IP::isPublic
+ */
+ public function testPrivateIPs() {
+ $private = array( 'fc00::3', 'fc00::ff', '::1', '10.0.0.1', '172.16.0.1', '192.168.0.1' );
+ foreach ( $private as $p ) {
+ $this->assertFalse( IP::isPublic( $p ), "$p is not a public IP address" );
+ }
+ $public = array( '2001:5c0:1000:a::133', 'fc::3' );
+ foreach ( $public as $p ) {
+ $this->assertTrue( IP::isPublic( $p ), "$p is a public IP address" );
+ }
+ }
+
+ // Private wrapper used to test CIDR Parsing.
+ private function assertFalseCIDR( $CIDR, $msg='' ) {
+ $ff = array( false, false );
+ $this->assertEquals( $ff, IP::parseCIDR( $CIDR ), $msg );
+ }
+
+ // Private wrapper to test network shifting using only dot notation
+ private function assertNet( $expected, $CIDR ) {
+ $parse = IP::parseCIDR( $CIDR );
+ $this->assertEquals( $expected, long2ip( $parse[0] ), "network shifting $CIDR" );
+ }
+
+ /**
+ * @covers IP::hexToQuad
+ */
+ public function testHexToQuad() {
+ $this->assertEquals( '0.0.0.1' , IP::hexToQuad( '00000001' ) );
+ $this->assertEquals( '255.0.0.0' , IP::hexToQuad( 'FF000000' ) );
+ $this->assertEquals( '255.255.255.255', IP::hexToQuad( 'FFFFFFFF' ) );
+ $this->assertEquals( '10.188.222.255' , IP::hexToQuad( '0ABCDEFF' ) );
+ // hex not left-padded...
+ $this->assertEquals( '0.0.0.0' , IP::hexToQuad( '0' ) );
+ $this->assertEquals( '0.0.0.1' , IP::hexToQuad( '1' ) );
+ $this->assertEquals( '0.0.0.255' , IP::hexToQuad( 'FF' ) );
+ $this->assertEquals( '0.0.255.0' , IP::hexToQuad( 'FF00' ) );
+ }
+
+ /**
+ * @covers IP::hexToOctet
+ */
+ public function testHexToOctet() {
+ $this->assertEquals( '0:0:0:0:0:0:0:1',
+ IP::hexToOctet( '00000000000000000000000000000001' ) );
+ $this->assertEquals( '0:0:0:0:0:0:FF:3',
+ IP::hexToOctet( '00000000000000000000000000FF0003' ) );
+ $this->assertEquals( '0:0:0:0:0:0:FF00:6',
+ IP::hexToOctet( '000000000000000000000000FF000006' ) );
+ $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF',
+ IP::hexToOctet( '000000000000000000000000FCCFFAFF' ) );
+ $this->assertEquals( 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
+ IP::hexToOctet( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' ) );
+ // hex not left-padded...
+ $this->assertEquals( '0:0:0:0:0:0:0:0' , IP::hexToOctet( '0' ) );
+ $this->assertEquals( '0:0:0:0:0:0:0:1' , IP::hexToOctet( '1' ) );
+ $this->assertEquals( '0:0:0:0:0:0:0:FF' , IP::hexToOctet( 'FF' ) );
+ $this->assertEquals( '0:0:0:0:0:0:0:FFD0' , IP::hexToOctet( 'FFD0' ) );
+ $this->assertEquals( '0:0:0:0:0:0:FA00:0' , IP::hexToOctet( 'FA000000' ) );
+ $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF', IP::hexToOctet( 'FCCFFAFF' ) );
+ }
+
+ /*
+ * IP::parseCIDR() returns an array containing a signed IP address
+ * representing the network mask and the bit mask.
+ * @covers IP::parseCIDR
+ */
+ function testCIDRParsing() {
+ $this->assertFalseCIDR( '192.0.2.0' , "missing mask" );
+ $this->assertFalseCIDR( '192.0.2.0/', "missing bitmask" );
+
+ // Verify if statement
+ $this->assertFalseCIDR( '256.0.0.0/32', "invalid net" );
+ $this->assertFalseCIDR( '192.0.2.0/AA', "mask not numeric" );
+ $this->assertFalseCIDR( '192.0.2.0/-1', "mask < 0" );
+ $this->assertFalseCIDR( '192.0.2.0/33', "mask > 32" );
+
+ // Check internal logic
+ # 0 mask always result in array(0,0)
+ $this->assertEquals( array( 0, 0 ), IP::parseCIDR('192.0.0.2/0') );
+ $this->assertEquals( array( 0, 0 ), IP::parseCIDR('0.0.0.0/0') );
+ $this->assertEquals( array( 0, 0 ), IP::parseCIDR('255.255.255.255/0') );
+
+ // @todo FIXME: Add more tests.
+
+ # This part test network shifting
+ $this->assertNet( '192.0.0.0' , '192.0.0.2/24' );
+ $this->assertNet( '192.168.5.0', '192.168.5.13/24');
+ $this->assertNet( '10.0.0.160' , '10.0.0.161/28' );
+ $this->assertNet( '10.0.0.0' , '10.0.0.3/28' );
+ $this->assertNet( '10.0.0.0' , '10.0.0.3/30' );
+ $this->assertNet( '10.0.0.4' , '10.0.0.4/30' );
+ $this->assertNet( '172.17.32.0', '172.17.35.48/21' );
+ $this->assertNet( '10.128.0.0' , '10.135.0.0/9' );
+ $this->assertNet( '134.0.0.0' , '134.0.5.1/8' );
+ }
+
+
+ /**
+ * @covers IP::canonicalize
+ */
+ public function testIPCanonicalizeOnValidIp() {
+ $this->assertEquals( '192.0.2.152', IP::canonicalize( '192.0.2.152' ),
+ 'Canonicalization of a valid IP returns it unchanged' );
+ }
+
+ /**
+ * @covers IP::canonicalize
+ */
+ public function testIPCanonicalizeMappedAddress() {
+ $this->assertEquals(
+ '192.0.2.152',
+ IP::canonicalize( '::ffff:192.0.2.152' )
+ );
+ $this->assertEquals(
+ '192.0.2.152',
+ IP::canonicalize( '::192.0.2.152' )
+ );
+ }
+
+ /**
+ * Issues there are most probably from IP::toHex() or IP::parseRange()
+ * @covers IP::isInRange
+ * @dataProvider provideIPsAndRanges
+ */
+ public function testIPIsInRange( $expected, $addr, $range, $message = '' ) {
+ $this->assertEquals(
+ $expected,
+ IP::isInRange( $addr, $range ),
+ $message
+ );
+ }
+
+ /** Provider for testIPIsInRange() */
+ function provideIPsAndRanges() {
+ # Format: (expected boolean, address, range, optional message)
+ return array(
+ # IPv4
+ array( true , '192.0.2.0' , '192.0.2.0/24', 'Network address' ),
+ array( true , '192.0.2.77' , '192.0.2.0/24', 'Simple address' ),
+ array( true , '192.0.2.255' , '192.0.2.0/24', 'Broadcast address' ),
+
+ array( false, '0.0.0.0' , '192.0.2.0/24' ),
+ array( false, '255.255.255' , '192.0.2.0/24' ),
+
+ # IPv6
+ array( false, '::1' , '2001:DB8::/32' ),
+ array( false, '::' , '2001:DB8::/32' ),
+ array( false, 'FE80::1', '2001:DB8::/32' ),
+
+ array( true , '2001:DB8::' , '2001:DB8::/32' ),
+ array( true , '2001:0DB8::' , '2001:DB8::/32' ),
+ array( true , '2001:DB8::1' , '2001:DB8::/32' ),
+ array( true , '2001:0DB8::1', '2001:DB8::/32' ),
+ array( true , '2001:0DB8:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
+ '2001:DB8::/32' ),
+
+ array( false, '2001:0DB8:F::', '2001:DB8::/96' ),
+ );
+ }
+
+ /**
+ * Test for IP::splitHostAndPort().
+ * @dataProvider provideSplitHostAndPort
+ */
+ function testSplitHostAndPort( $expected, $input, $description ) {
+ $this->assertEquals( $expected, IP::splitHostAndPort( $input ), $description );
+ }
+
+ /**
+ * Provider for IP::splitHostAndPort()
+ */
+ function provideSplitHostAndPort() {
+ return array(
+ array( false, '[', 'Unclosed square bracket' ),
+ array( false, '[::', 'Unclosed square bracket 2' ),
+ array( array( '::', false ), '::', 'Bare IPv6 0' ),
+ array( array( '::1', false ), '::1', 'Bare IPv6 1' ),
+ array( array( '::', false ), '[::]', 'Bracketed IPv6 0' ),
+ array( array( '::1', false ), '[::1]', 'Bracketed IPv6 1' ),
+ array( array( '::1', 80 ), '[::1]:80', 'Bracketed IPv6 with port' ),
+ array( false, '::x', 'Double colon but no IPv6' ),
+ array( array( 'x', 80 ), 'x:80', 'Hostname and port' ),
+ array( false, 'x:x', 'Hostname and invalid port' ),
+ array( array( 'x', false ), 'x', 'Plain hostname' )
+ );
+ }
+
+ /**
+ * Test for IP::combineHostAndPort()
+ * @dataProvider provideCombineHostAndPort
+ */
+ function testCombineHostAndPort( $expected, $input, $description ) {
+ list( $host, $port, $defaultPort ) = $input;
+ $this->assertEquals(
+ $expected,
+ IP::combineHostAndPort( $host, $port, $defaultPort ),
+ $description );
+ }
+
+ /**
+ * Provider for IP::combineHostAndPort()
+ */
+ function provideCombineHostAndPort() {
+ return array(
+ array( '[::1]', array( '::1', 2, 2 ), 'IPv6 default port' ),
+ array( '[::1]:2', array( '::1', 2, 3 ), 'IPv6 non-default port' ),
+ array( 'x', array( 'x', 2, 2 ), 'Normal default port' ),
+ array( 'x:2', array( 'x', 2, 3 ), 'Normal non-default port' ),
+ );
+ }
+
+ /**
+ * Test for IP::sanitizeRange()
+ * @dataProvider provideIPCIDRs
+ */
+ function testSanitizeRange( $input, $expected, $description ) {
+ $this->assertEquals( $expected, IP::sanitizeRange( $input ), $description );
+ }
+
+ /**
+ * Provider for IP::testSanitizeRange()
+ */
+ function provideIPCIDRs() {
+ return array(
+ array( '35.56.31.252/16', '35.56.0.0/16', 'IPv4 range' ),
+ array( '135.16.21.252/24', '135.16.21.0/24', 'IPv4 range' ),
+ array( '5.36.71.252/32', '5.36.71.252/32', 'IPv4 silly range' ),
+ array( '5.36.71.252', '5.36.71.252', 'IPv4 non-range' ),
+ array( '0:1:2:3:4:c5:f6:7/96', '0:1:2:3:4:C5:0:0/96', 'IPv6 range' ),
+ array( '0:1:2:3:4:5:6:7/120', '0:1:2:3:4:5:6:0/120', 'IPv6 range' ),
+ array( '0:e1:2:3:4:5:e6:7/128', '0:E1:2:3:4:5:E6:7/128', 'IPv6 silly range' ),
+ array( '0:c1:A2:3:4:5:c6:7', '0:C1:A2:3:4:5:C6:7', 'IPv6 non range' ),
+ );
+ }
+}
diff --git a/tests/phpunit/includes/ImageFunctionsTest.php b/tests/phpunit/includes/ImageFunctionsTest.php
new file mode 100644
index 00000000..cb7e67f3
--- /dev/null
+++ b/tests/phpunit/includes/ImageFunctionsTest.php
@@ -0,0 +1,48 @@
+<?php
+
+class ImageFunctionsTest extends MediaWikiTestCase {
+ function testFitBoxWidth() {
+ $vals = array(
+ array(
+ 'width' => 50,
+ 'height' => 50,
+ 'tests' => array(
+ 50 => 50,
+ 17 => 17,
+ 18 => 18 ) ),
+ array(
+ 'width' => 366,
+ 'height' => 300,
+ 'tests' => array(
+ 50 => 61,
+ 17 => 21,
+ 18 => 22 ) ),
+ array(
+ 'width' => 300,
+ 'height' => 366,
+ 'tests' => array(
+ 50 => 41,
+ 17 => 14,
+ 18 => 15 ) ),
+ array(
+ 'width' => 100,
+ 'height' => 400,
+ 'tests' => array(
+ 50 => 12,
+ 17 => 4,
+ 18 => 4 ) ) );
+ foreach ( $vals as $row ) {
+ extract( $row );
+ foreach ( $tests as $max => $expected ) {
+ $y = round( $expected * $height / $width );
+ $result = wfFitBoxWidth( $width, $height, $max );
+ $y2 = round( $result * $height / $width );
+ $this->assertEquals( $expected,
+ $result,
+ "($width, $height, $max) wanted: {$expected}x$y, got: {$result}x$y2" );
+ }
+ }
+ }
+}
+
+
diff --git a/tests/phpunit/includes/JsonTest.php b/tests/phpunit/includes/JsonTest.php
new file mode 100644
index 00000000..75dd18d5
--- /dev/null
+++ b/tests/phpunit/includes/JsonTest.php
@@ -0,0 +1,33 @@
+<?php
+
+class JsonTest extends MediaWikiTestCase {
+
+ function testPhpBug46944Test() {
+
+ $this->assertNotEquals(
+ '\ud840\udc00',
+ strtolower( FormatJson::encode( "\xf0\xa0\x80\x80" ) ),
+ 'Test encoding an broken json_encode character (U+20000)'
+ );
+
+
+ }
+
+ function testDecodeVarTypes() {
+
+ $this->assertInternalType(
+ 'object',
+ FormatJson::decode( '{"Name": "Cheeso", "Rank": 7}' ),
+ 'Default to object'
+ );
+
+ $this->assertInternalType(
+ 'array',
+ FormatJson::decode( '{"Name": "Cheeso", "Rank": 7}', true ),
+ 'Optional array'
+ );
+
+ }
+
+}
+
diff --git a/tests/phpunit/includes/LanguageConverterTest.php b/tests/phpunit/includes/LanguageConverterTest.php
new file mode 100644
index 00000000..baf28b07
--- /dev/null
+++ b/tests/phpunit/includes/LanguageConverterTest.php
@@ -0,0 +1,130 @@
+<?php
+
+class LanguageConverterTest extends MediaWikiLangTestCase {
+ protected $lang = null;
+ protected $lc = null;
+
+ function setUp() {
+ parent::setUp();
+ global $wgMemc, $wgRequest, $wgUser, $wgContLang;
+
+ $wgUser = new User;
+ $wgRequest = new FauxRequest( array() );
+ $wgMemc = new EmptyBagOStuff;
+ $wgContLang = Language::factory( 'tg' );
+ $this->lang = new LanguageToTest();
+ $this->lc = new TestConverter( $this->lang, 'tg',
+ array( 'tg', 'tg-latn' ) );
+ }
+
+ function tearDown() {
+ global $wgMemc;
+ unset( $wgMemc );
+ unset( $this->lc );
+ unset( $this->lang );
+ parent::tearDown();
+ }
+
+ function testGetPreferredVariantDefaults() {
+ $this->assertEquals( 'tg', $this->lc->getPreferredVariant() );
+ }
+
+ function testGetPreferredVariantHeaders() {
+ global $wgRequest;
+ $wgRequest->setHeader( 'Accept-Language', 'tg-latn' );
+
+ $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
+ }
+
+ function testGetPreferredVariantHeaderWeight() {
+ global $wgRequest;
+ $wgRequest->setHeader( 'Accept-Language', 'tg;q=1' );
+
+ $this->assertEquals( 'tg', $this->lc->getPreferredVariant() );
+ }
+
+ function testGetPreferredVariantHeaderWeight2() {
+ global $wgRequest;
+ $wgRequest->setHeader( 'Accept-Language', 'tg-latn;q=1' );
+
+ $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
+ }
+
+ function testGetPreferredVariantHeaderMulti() {
+ global $wgRequest;
+ $wgRequest->setHeader( 'Accept-Language', 'en, tg-latn;q=1' );
+
+ $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
+ }
+
+ function testGetPreferredVariantUserOption() {
+ global $wgUser;
+
+ $wgUser = new User;
+ $wgUser->load(); // from 'defaults'
+ $wgUser->mId = 1;
+ $wgUser->mDataLoaded = true;
+ $wgUser->mOptionsLoaded = true;
+ $wgUser->setOption( 'variant', 'tg-latn' );
+
+ $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
+ }
+
+ function testGetPreferredVariantHeaderUserVsUrl() {
+ global $wgRequest, $wgUser, $wgContLang;
+
+ $wgContLang = Language::factory( 'tg-latn' );
+ $wgRequest->setVal( 'variant', 'tg' );
+ $wgUser = User::newFromId( "admin" );
+ $wgUser->setId( 1 );
+ $wgUser->mFrom = 'defaults';
+ $wgUser->mOptionsLoaded = true;
+ $wgUser->setOption( 'variant', 'tg-latn' ); // The user's data is ignored
+ // because the variant is set in the URL.
+ $this->assertEquals( 'tg', $this->lc->getPreferredVariant() );
+ }
+
+
+ function testGetPreferredVariantDefaultLanguageVariant() {
+ global $wgDefaultLanguageVariant;
+
+ $wgDefaultLanguageVariant = 'tg-latn';
+ $this->assertEquals( 'tg-latn', $this->lc->getPreferredVariant() );
+ }
+
+ function testGetPreferredVariantDefaultLanguageVsUrlVariant() {
+ global $wgDefaultLanguageVariant, $wgRequest, $wgContLang;
+
+ $wgContLang = Language::factory( 'tg-latn' );
+ $wgDefaultLanguageVariant = 'tg';
+ $wgRequest->setVal( 'variant', null );
+ $this->assertEquals( 'tg', $this->lc->getPreferredVariant() );
+ }
+}
+
+/**
+ * Test converter (from Tajiki to latin orthography)
+ */
+class TestConverter extends LanguageConverter {
+ private $table = array(
+ 'б' => 'b',
+ 'в' => 'v',
+ 'г' => 'g',
+ );
+
+ function loadDefaultTables() {
+ $this->mTables = array(
+ 'tg-latn' => new ReplacementArray( $this->table ),
+ 'tg' => new ReplacementArray()
+ );
+ }
+
+}
+
+class LanguageToTest extends Language {
+ function __construct() {
+ parent::__construct();
+ $variants = array( 'tg', 'tg-latn' );
+ $this->mConverter = new TestConverter( $this, 'tg', $variants );
+ }
+}
diff --git a/tests/phpunit/includes/LicensesTest.php b/tests/phpunit/includes/LicensesTest.php
new file mode 100644
index 00000000..e467f3cd
--- /dev/null
+++ b/tests/phpunit/includes/LicensesTest.php
@@ -0,0 +1,22 @@
+<?php
+
+class LicensesTest extends MediaWikiTestCase {
+
+ function testLicenses() {
+ $str = "
+* Free licenses:
+** GFDL|Debian disagrees
+";
+
+ $lc = new Licenses( array(
+ 'fieldname' => 'FooField',
+ 'type' => 'select',
+ 'section' => 'description',
+ 'id' => 'wpLicense',
+ 'label' => 'A label text', # Note can't test label-message because $wgOut is not defined
+ 'name' => 'AnotherName',
+ 'licenses' => $str,
+ ) );
+ $this->assertThat( $lc, $this->isInstanceOf( 'Licenses' ) );
+ }
+}
diff --git a/tests/phpunit/includes/LocalFileTest.php b/tests/phpunit/includes/LocalFileTest.php
new file mode 100644
index 00000000..e08d4d7e
--- /dev/null
+++ b/tests/phpunit/includes/LocalFileTest.php
@@ -0,0 +1,99 @@
+<?php
+
+/**
+ * These tests should work regardless of $wgCapitalLinks
+ * @group Database
+ */
+
+class LocalFileTest extends MediaWikiTestCase {
+ function setUp() {
+ global $wgCapitalLinks;
+
+ $wgCapitalLinks = true;
+ $info = array(
+ 'name' => 'test',
+ 'directory' => '/testdir',
+ 'url' => '/testurl',
+ 'hashLevels' => 2,
+ 'transformVia404' => false,
+ );
+ $this->repo_hl0 = new LocalRepo( array( 'hashLevels' => 0 ) + $info );
+ $this->repo_hl2 = new LocalRepo( array( 'hashLevels' => 2 ) + $info );
+ $this->repo_lc = new LocalRepo( array( 'initialCapital' => false ) + $info );
+ $this->file_hl0 = $this->repo_hl0->newFile( 'test!' );
+ $this->file_hl2 = $this->repo_hl2->newFile( 'test!' );
+ $this->file_lc = $this->repo_lc->newFile( 'test!' );
+ }
+
+ function testGetHashPath() {
+ $this->assertEquals( '', $this->file_hl0->getHashPath() );
+ $this->assertEquals( 'a/a2/', $this->file_hl2->getHashPath() );
+ $this->assertEquals( 'c/c4/', $this->file_lc->getHashPath() );
+ }
+
+ function testGetRel() {
+ $this->assertEquals( 'Test!', $this->file_hl0->getRel() );
+ $this->assertEquals( 'a/a2/Test!', $this->file_hl2->getRel() );
+ $this->assertEquals( 'c/c4/test!', $this->file_lc->getRel() );
+ }
+
+ function testGetUrlRel() {
+ $this->assertEquals( 'Test%21', $this->file_hl0->getUrlRel() );
+ $this->assertEquals( 'a/a2/Test%21', $this->file_hl2->getUrlRel() );
+ $this->assertEquals( 'c/c4/test%21', $this->file_lc->getUrlRel() );
+ }
+
+ function testGetArchivePath() {
+ $this->assertEquals( '/testdir/archive', $this->file_hl0->getArchivePath() );
+ $this->assertEquals( '/testdir/archive/a/a2', $this->file_hl2->getArchivePath() );
+ $this->assertEquals( '/testdir/archive/!', $this->file_hl0->getArchivePath( '!' ) );
+ $this->assertEquals( '/testdir/archive/a/a2/!', $this->file_hl2->getArchivePath( '!' ) );
+ }
+
+ function testGetThumbPath() {
+ $this->assertEquals( '/testdir/thumb/Test!', $this->file_hl0->getThumbPath() );
+ $this->assertEquals( '/testdir/thumb/a/a2/Test!', $this->file_hl2->getThumbPath() );
+ $this->assertEquals( '/testdir/thumb/Test!/x', $this->file_hl0->getThumbPath( 'x' ) );
+ $this->assertEquals( '/testdir/thumb/a/a2/Test!/x', $this->file_hl2->getThumbPath( 'x' ) );
+ }
+
+ function testGetArchiveUrl() {
+ $this->assertEquals( '/testurl/archive', $this->file_hl0->getArchiveUrl() );
+ $this->assertEquals( '/testurl/archive/a/a2', $this->file_hl2->getArchiveUrl() );
+ $this->assertEquals( '/testurl/archive/%21', $this->file_hl0->getArchiveUrl( '!' ) );
+ $this->assertEquals( '/testurl/archive/a/a2/%21', $this->file_hl2->getArchiveUrl( '!' ) );
+ }
+
+ function testGetThumbUrl() {
+ $this->assertEquals( '/testurl/thumb/Test%21', $this->file_hl0->getThumbUrl() );
+ $this->assertEquals( '/testurl/thumb/a/a2/Test%21', $this->file_hl2->getThumbUrl() );
+ $this->assertEquals( '/testurl/thumb/Test%21/x', $this->file_hl0->getThumbUrl( 'x' ) );
+ $this->assertEquals( '/testurl/thumb/a/a2/Test%21/x', $this->file_hl2->getThumbUrl( 'x' ) );
+ }
+
+ function testGetArchiveVirtualUrl() {
+ $this->assertEquals( 'mwrepo://test/public/archive', $this->file_hl0->getArchiveVirtualUrl() );
+ $this->assertEquals( 'mwrepo://test/public/archive/a/a2', $this->file_hl2->getArchiveVirtualUrl() );
+ $this->assertEquals( 'mwrepo://test/public/archive/%21', $this->file_hl0->getArchiveVirtualUrl( '!' ) );
+ $this->assertEquals( 'mwrepo://test/public/archive/a/a2/%21', $this->file_hl2->getArchiveVirtualUrl( '!' ) );
+ }
+
+ function testGetThumbVirtualUrl() {
+ $this->assertEquals( 'mwrepo://test/thumb/Test%21', $this->file_hl0->getThumbVirtualUrl() );
+ $this->assertEquals( 'mwrepo://test/thumb/a/a2/Test%21', $this->file_hl2->getThumbVirtualUrl() );
+ $this->assertEquals( 'mwrepo://test/thumb/Test%21/%21', $this->file_hl0->getThumbVirtualUrl( '!' ) );
+ $this->assertEquals( 'mwrepo://test/thumb/a/a2/Test%21/%21', $this->file_hl2->getThumbVirtualUrl( '!' ) );
+ }
+
+ function testGetUrl() {
+ $this->assertEquals( '/testurl/Test%21', $this->file_hl0->getUrl() );
+ $this->assertEquals( '/testurl/a/a2/Test%21', $this->file_hl2->getUrl() );
+ }
+
+ function testWfLocalFile() {
+ $file = wfLocalFile( "File:Some_file_that_probably_doesn't exist.png" );
+ $this->assertThat( $file, $this->isInstanceOf( 'LocalFile' ), 'wfLocalFile() returns LocalFile for valid Titles' );
+ }
+}
+
+
diff --git a/tests/phpunit/includes/MWFunctionTest.php b/tests/phpunit/includes/MWFunctionTest.php
new file mode 100644
index 00000000..ed5e7602
--- /dev/null
+++ b/tests/phpunit/includes/MWFunctionTest.php
@@ -0,0 +1,86 @@
+<?php
+
+class MWFunctionTest extends MediaWikiTestCase {
+
+ function testCallUserFuncWorkarounds() {
+
+ $this->assertEquals(
+ call_user_func( array( 'MWFunctionTest', 'someMethod' ) ),
+ MWFunction::call( 'MWFunctionTest::someMethod' )
+ );
+ $this->assertEquals(
+ call_user_func( array( 'MWFunctionTest', 'someMethod' ), 'foo', 'bar', 'baz' ),
+ MWFunction::call( 'MWFunctionTest::someMethod', 'foo', 'bar', 'baz' )
+ );
+
+
+
+ $this->assertEquals(
+ call_user_func_array( array( 'MWFunctionTest', 'someMethod' ), array() ),
+ MWFunction::callArray( 'MWFunctionTest::someMethod', array() )
+ );
+ $this->assertEquals(
+ call_user_func_array( array( 'MWFunctionTest', 'someMethod' ), array( 'foo', 'bar', 'baz' ) ),
+ MWFunction::callArray( 'MWFunctionTest::someMethod', array( 'foo', 'bar', 'baz' ) )
+ );
+
+ }
+
+ function testNewObjFunction() {
+
+ $arg1 = 'Foo';
+ $arg2 = 'Bar';
+ $arg3 = array( 'Baz' );
+ $arg4 = new ExampleObject;
+
+ $args = array( $arg1, $arg2, $arg3, $arg4 );
+
+ $newObject = new MWBlankClass( $arg1, $arg2, $arg3, $arg4 );
+
+ $this->assertEquals(
+ MWFunction::newObj( 'MWBlankClass', $args )->args,
+ $newObject->args
+ );
+
+ $this->assertEquals(
+ MWFunction::newObj( 'MWBlankClass', $args, true )->args,
+ $newObject->args,
+ 'Works even with PHP version < 5.1.3'
+ );
+
+ }
+
+ /**
+ * @expectedException MWException
+ */
+ function testCallingParentFails() {
+
+ MWFunction::call( 'parent::foo' );
+ }
+
+ /**
+ * @expectedException MWException
+ */
+ function testCallingSelfFails() {
+
+ MWFunction::call( 'self::foo' );
+ }
+
+ public static function someMethod() {
+ return func_get_args();
+ }
+
+}
+
+class MWBlankClass {
+
+ public $args = array();
+
+ function __construct( $arg1, $arg2, $arg3, $arg4 ) {
+ $this->args = array( $arg1, $arg2, $arg3, $arg4 );
+ }
+
+}
+
+class ExampleObject {
+}
diff --git a/tests/phpunit/includes/MWNamespaceTest.php b/tests/phpunit/includes/MWNamespaceTest.php
new file mode 100644
index 00000000..462afc24
--- /dev/null
+++ b/tests/phpunit/includes/MWNamespaceTest.php
@@ -0,0 +1,460 @@
+<?php
+/**
+ * @author Ashar Voultoiz
+ * @copyright Copyright © 2011, Ashar Voultoiz
+ * @file
+ */
+
+/**
+ * Test class for MWNamespace.
+ * Generated by PHPUnit on 2011-02-20 at 21:01:55.
+ *
+ */
+class MWNamespaceTest extends MediaWikiTestCase {
+ /**
+ * Sets up the fixture, for example, opens a network connection.
+ * This method is called before a test is executed.
+ */
+ protected function setUp() {
+ }
+
+ /**
+ * Tears down the fixture, for example, closes a network connection.
+ * This method is called after a test is executed.
+ */
+ protected function tearDown() {
+ }
+
+
+#### START OF TESTS #########################################################
+
+ /**
+ * @todo Write more texts, handle $wgAllowImageMoving setting
+ */
+ public function testIsMovable() {
+ $this->assertFalse( MWNamespace::isMovable( NS_CATEGORY ) );
+ # @todo FIXME: Write more tests!!
+ }
+
+ /**
+ * Please make sure to change testIsTalk() if you change the assertions below
+ */
+ public function testIsMain() {
+ // Special namespaces
+ $this->assertTrue( MWNamespace::isMain( NS_MEDIA ) );
+ $this->assertTrue( MWNamespace::isMain( NS_SPECIAL ) );
+
+ // Subject pages
+ $this->assertTrue( MWNamespace::isMain( NS_MAIN ) );
+ $this->assertTrue( MWNamespace::isMain( NS_USER ) );
+ $this->assertTrue( MWNamespace::isMain( 100 ) ); # user defined
+
+ // Talk pages
+ $this->assertFalse( MWNamespace::isMain( NS_TALK ) );
+ $this->assertFalse( MWNamespace::isMain( NS_USER_TALK ) );
+ $this->assertFalse( MWNamespace::isMain( 101 ) ); # user defined
+ }
+
+ /**
+ * Reverse of testIsMain().
+ * Please update testIsMain() if you change assertions below
+ */
+ public function testIsTalk() {
+ // Special namespaces
+ $this->assertFalse( MWNamespace::isTalk( NS_MEDIA ) );
+ $this->assertFalse( MWNamespace::isTalk( NS_SPECIAL ) );
+
+ // Subject pages
+ $this->assertFalse( MWNamespace::isTalk( NS_MAIN ) );
+ $this->assertFalse( MWNamespace::isTalk( NS_USER ) );
+ $this->assertFalse( MWNamespace::isTalk( 100 ) ); # user defined
+
+ // Talk pages
+ $this->assertTrue( MWNamespace::isTalk( NS_TALK ) );
+ $this->assertTrue( MWNamespace::isTalk( NS_USER_TALK ) );
+ $this->assertTrue( MWNamespace::isTalk( 101 ) ); # user defined
+ }
+
+ /**
+ * Regular getTalk() calls
+ * Namespaces without a talk page (NS_MEDIA, NS_SPECIAL) are tested in
+ * the function testGetTalkExceptions()
+ */
+ public function testGetTalk() {
+ $this->assertEquals( NS_TALK, MWNamespace::getTalk( NS_MAIN ) );
+ }
+
+ /**
+ * Exceptions with getTalk()
+ * NS_MEDIA does not have talk pages. MediaWiki raise an exception for them.
+ * @expectedException MWException
+ */
+ public function testGetTalkExceptionsForNsMedia() {
+ $this->assertNull( MWNamespace::getTalk( NS_MEDIA ) );
+ }
+
+ /**
+ * Exceptions with getTalk()
+ * NS_SPECIAL does not have talk pages. MediaWiki raise an exception for them.
+ * @expectedException MWException
+ */
+ public function testGetTalkExceptionsForNsSpecial() {
+ $this->assertNull( MWNamespace::getTalk( NS_SPECIAL ) );
+ }
+
+ /**
+ * Regular getAssociated() calls
+ * Namespaces without an associated page (NS_MEDIA, NS_SPECIAL) are tested in
+ * the function testGetAssociatedExceptions()
+ */
+ public function testGetAssociated() {
+ $this->assertEquals( NS_TALK, MWNamespace::getAssociated( NS_MAIN ) );
+ $this->assertEquals( NS_MAIN, MWNamespace::getAssociated( NS_TALK ) );
+
+ }
+
+ ### Exceptions with getAssociated()
+ ### NS_MEDIA and NS_SPECIAL do not have talk pages. MediaWiki raises
+ ### an exception for them.
+ /**
+ * @expectedException MWException
+ */
+ public function testGetAssociatedExceptionsForNsMedia() {
+ $this->assertNull( MWNamespace::getAssociated( NS_MEDIA ) );
+ }
+
+ /**
+ * @expectedException MWException
+ */
+ public function testGetAssociatedExceptionsForNsSpecial() {
+ $this->assertNull( MWNamespace::getAssociated( NS_SPECIAL ) );
+ }
+
+ /**
+ */
+ public function testGetSubject() {
+ // Special namespaces are their own subjects
+ $this->assertEquals( NS_MEDIA, MWNamespace::getSubject( NS_MEDIA ) );
+ $this->assertEquals( NS_SPECIAL, MWNamespace::getSubject( NS_SPECIAL ) );
+
+ $this->assertEquals( NS_MAIN, MWNamespace::getSubject( NS_TALK ) );
+ $this->assertEquals( NS_USER, MWNamespace::getSubject( NS_USER_TALK ) );
+ }
+
+ /**
+ * @todo Implement testExists().
+ */
+ public function testExists() {
+ // Remove the following lines when you implement this test.
+ $this->markTestIncomplete(
+ 'This test has not been implemented yet. Rely on $wgCanonicalNamespaces.'
+ );
+ }
+
+ /**
+ * @todo Implement testGetCanonicalNamespaces().
+ */
+ public function testGetCanonicalNamespaces() {
+ // Remove the following lines when you implement this test.
+ $this->markTestIncomplete(
+ 'This test has not been implemented yet. Rely on $wgCanonicalNamespaces.'
+ );
+ }
+
+ /**
+ * @todo Implement testGetCanonicalName().
+ */
+ public function testGetCanonicalName() {
+ // Remove the following lines when you implement this test.
+ $this->markTestIncomplete(
+ 'This test has not been implemented yet. Rely on $wgCanonicalNamespaces.'
+ );
+ }
+
+ /**
+ * @todo Implement testGetCanonicalIndex().
+ */
+ public function testGetCanonicalIndex() {
+ // Remove the following lines when you implement this test.
+ $this->markTestIncomplete(
+ 'This test has not been implemented yet. Rely on $wgCanonicalNamespaces.'
+ );
+ }
+
+ /**
+ * @todo Implement testGetValidNamespaces().
+ */
+ public function testGetValidNamespaces() {
+ // Remove the following lines when you implement this test.
+ $this->markTestIncomplete(
+ 'This test has not been implemented yet. Rely on $wgCanonicalNamespaces.'
+ );
+ }
+
+ /**
+ */
+ public function testCanTalk() {
+ $this->assertFalse( MWNamespace::canTalk( NS_MEDIA ) );
+ $this->assertFalse( MWNamespace::canTalk( NS_SPECIAL ) );
+
+ $this->assertTrue( MWNamespace::canTalk( NS_MAIN ) );
+ $this->assertTrue( MWNamespace::canTalk( NS_TALK ) );
+ $this->assertTrue( MWNamespace::canTalk( NS_USER ) );
+ $this->assertTrue( MWNamespace::canTalk( NS_USER_TALK ) );
+
+ // User defined namespaces
+ $this->assertTrue( MWNamespace::canTalk( 100 ) );
+ $this->assertTrue( MWNamespace::canTalk( 101 ) );
+ }
+
+ /**
+ */
+ public function testIsContent() {
+ // NS_MAIN is a content namespace per DefaultSettings.php
+ // and per function definition.
+ $this->assertTrue( MWNamespace::isContent( NS_MAIN ) );
+
+ // Other namespaces which are not expected to be content
+ $this->assertFalse( MWNamespace::isContent( NS_MEDIA ) );
+ $this->assertFalse( MWNamespace::isContent( NS_SPECIAL ) );
+ $this->assertFalse( MWNamespace::isContent( NS_TALK ) );
+ $this->assertFalse( MWNamespace::isContent( NS_USER ) );
+ $this->assertFalse( MWNamespace::isContent( NS_CATEGORY ) );
+ // User defined namespace:
+ $this->assertFalse( MWNamespace::isContent( 100 ) );
+ }
+
+ /**
+ * Similar to testIsContent() but alters the $wgContentNamespaces
+ * global variable.
+ */
+ public function testIsContentWithAdditionsInWgContentNamespaces() {
+ // NS_MAIN is a content namespace per DefaultSettings.php
+ // and per function definition.
+ $this->assertTrue( MWNamespace::isContent( NS_MAIN ) );
+
+ // Tests that user defined namespace #252 is not content:
+ $this->assertFalse( MWNamespace::isContent( 252 ) );
+
+ # @todo FIXME: Is global saving really required for PHPUnit?
+ // Bless namespace # 252 as a content namespace
+ global $wgContentNamespaces;
+ $savedGlobal = $wgContentNamespaces;
+ $wgContentNamespaces[] = 252;
+ $this->assertTrue( MWNamespace::isContent( 252 ) );
+
+ // Makes sure NS_MAIN was not impacted
+ $this->assertTrue( MWNamespace::isContent( NS_MAIN ) );
+
+ // Restore global
+ $wgContentNamespaces = $savedGlobal;
+
+ // Verify namespaces after global restauration
+ $this->assertTrue( MWNamespace::isContent( NS_MAIN ) );
+ $this->assertFalse( MWNamespace::isContent( 252 ) );
+ }
+
+ public function testIsWatchable() {
+ // Specials namespaces are not watchable
+ $this->assertFalse( MWNamespace::isWatchable( NS_MEDIA ) );
+ $this->assertFalse( MWNamespace::isWatchable( NS_SPECIAL ) );
+
+ // Core defined namespaces are watchables
+ $this->assertTrue( MWNamespace::isWatchable( NS_MAIN ) );
+ $this->assertTrue( MWNamespace::isWatchable( NS_TALK ) );
+
+ // Additional, user defined namespaces are watchables
+ $this->assertTrue( MWNamespace::isWatchable( 100 ) );
+ $this->assertTrue( MWNamespace::isWatchable( 101 ) );
+ }
+
+ public function testHasSubpages() {
+ // Special namespaces:
+ $this->assertFalse( MWNamespace::hasSubpages( NS_MEDIA ) );
+ $this->assertFalse( MWNamespace::hasSubpages( NS_SPECIAL ) );
+
+ // namespaces without subpages
+ # save up global
+ global $wgNamespacesWithSubpages;
+ $saved = null;
+ if( array_key_exists( NS_MAIN, $wgNamespacesWithSubpages ) ) {
+ $saved = $wgNamespacesWithSubpages[NS_MAIN];
+ unset( $wgNamespacesWithSubpages[NS_MAIN] );
+ }
+
+ $this->assertFalse( MWNamespace::hasSubpages( NS_MAIN ) );
+
+ $wgNamespacesWithSubpages[NS_MAIN] = true;
+ $this->assertTrue( MWNamespace::hasSubpages( NS_MAIN ) );
+ $wgNamespacesWithSubpages[NS_MAIN] = false;
+ $this->assertFalse( MWNamespace::hasSubpages( NS_MAIN ) );
+
+ # restore global
+ if( $saved !== null ) {
+ $wgNamespacesWithSubpages[NS_MAIN] = $saved;
+ }
+
+ // Some namespaces with subpages
+ $this->assertTrue( MWNamespace::hasSubpages( NS_TALK ) );
+ $this->assertTrue( MWNamespace::hasSubpages( NS_USER ) );
+ $this->assertTrue( MWNamespace::hasSubpages( NS_USER_TALK ) );
+ }
+
+ /**
+ */
+ public function testGetContentNamespaces() {
+ $this->assertEquals(
+ array( NS_MAIN ),
+ MWNamespace::getcontentNamespaces(),
+ '$wgContentNamespaces is an array with only NS_MAIN by default'
+ );
+
+ global $wgContentNamespaces;
+
+ # test !is_array( $wgcontentNamespaces )
+ $wgContentNamespaces = '';
+ $this->assertEquals( NS_MAIN, MWNamespace::getcontentNamespaces() );
+ $wgContentNamespaces = false;
+ $this->assertEquals( NS_MAIN, MWNamespace::getcontentNamespaces() );
+ $wgContentNamespaces = null;
+ $this->assertEquals( NS_MAIN, MWNamespace::getcontentNamespaces() );
+ $wgContentNamespaces = 5;
+ $this->assertEquals( NS_MAIN, MWNamespace::getcontentNamespaces() );
+
+ # test $wgContentNamespaces === array()
+ $wgContentNamespaces = array();
+ $this->assertEquals( NS_MAIN, MWNamespace::getcontentNamespaces() );
+
+ # test !in_array( NS_MAIN, $wgContentNamespaces )
+ $wgContentNamespaces = array( NS_USER, NS_CATEGORY );
+ $this->assertEquals(
+ array( NS_MAIN, NS_USER, NS_CATEGORY ),
+ MWNamespace::getcontentNamespaces(),
+ 'NS_MAIN is forced in wgContentNamespaces even if unwanted'
+ );
+
+ # test other cases, return $wgcontentNamespaces as is
+ $wgContentNamespaces = array( NS_MAIN );
+ $this->assertEquals(
+ array( NS_MAIN ),
+ MWNamespace::getcontentNamespaces()
+ );
+
+ $wgContentNamespaces = array( NS_MAIN, NS_USER, NS_CATEGORY );
+ $this->assertEquals(
+ array( NS_MAIN, NS_USER, NS_CATEGORY ),
+ MWNamespace::getcontentNamespaces()
+ );
+
+ }
+
+ /**
+ * Some namespaces are always capitalized per code definition
+ * in MWNamespace::$alwaysCapitalizedNamespaces
+ */
+ public function testIsCapitalizedHardcodedAssertions() {
+ // NS_MEDIA and NS_FILE are treated the same
+ $this->assertEquals(
+ MWNamespace::isCapitalized( NS_MEDIA ),
+ MWNamespace::isCapitalized( NS_FILE ),
+ 'NS_MEDIA and NS_FILE have same capitalization rendering'
+ );
+
+ // Boths are capitalized by default
+ $this->assertTrue( MWNamespace::isCapitalized( NS_MEDIA ) );
+ $this->assertTrue( MWNamespace::isCapitalized( NS_FILE ) );
+
+ // Always capitalized namespaces
+ // @see MWNamespace::$alwaysCapitalizedNamespaces
+ $this->assertTrue( MWNamespace::isCapitalized( NS_SPECIAL ) );
+ $this->assertTrue( MWNamespace::isCapitalized( NS_USER ) );
+ $this->assertTrue( MWNamespace::isCapitalized( NS_MEDIAWIKI ) );
+ }
+
+ /**
+ * Follows up for testIsCapitalizedHardcodedAssertions() but alter the
+ * global $wgCapitalLink setting to have extended coverage.
+ *
+ * MWNamespace::isCapitalized() rely on two global settings:
+ * $wgCapitalLinkOverrides = array(); by default
+ * $wgCapitalLinks = true; by default
+ * This function test $wgCapitalLinks
+ *
+ * Global setting correctness is tested against the NS_PROJECT and
+ * NS_PROJECT_TALK namespaces since they are not hardcoded nor specials
+ */
+ public function testIsCapitalizedWithWgCapitalLinks() {
+ global $wgCapitalLinks;
+ // Save the global to easily reset to MediaWiki default settings
+ $savedGlobal = $wgCapitalLinks;
+
+ $wgCapitalLinks = true;
+ $this->assertTrue( MWNamespace::isCapitalized( NS_PROJECT ) );
+ $this->assertTrue( MWNamespace::isCapitalized( NS_PROJECT_TALK ) );
+
+ $wgCapitalLinks = false;
+ // hardcoded namespaces (see above function) are still capitalized:
+ $this->assertTrue( MWNamespace::isCapitalized( NS_SPECIAL ) );
+ $this->assertTrue( MWNamespace::isCapitalized( NS_USER ) );
+ $this->assertTrue( MWNamespace::isCapitalized( NS_MEDIAWIKI ) );
+ // setting is correctly applied
+ $this->assertFalse( MWNamespace::isCapitalized( NS_PROJECT ) );
+ $this->assertFalse( MWNamespace::isCapitalized( NS_PROJECT_TALK ) );
+
+ // reset global state:
+ $wgCapitalLinks = $savedGlobal;
+ }
+
+ /**
+ * Counter part for MWNamespace::testIsCapitalizedWithWgCapitalLinks() now
+ * testing the $wgCapitalLinkOverrides global.
+ *
+ * @todo split groups of assertions in autonomous testing functions
+ */
+ public function testIsCapitalizedWithWgCapitalLinkOverrides() {
+ global $wgCapitalLinkOverrides;
+ // Save the global to easily reset to MediaWiki default settings
+ $savedGlobal = $wgCapitalLinkOverrides;
+
+ // Test default settings
+ $this->assertTrue( MWNamespace::isCapitalized( NS_PROJECT ) );
+ $this->assertTrue( MWNamespace::isCapitalized( NS_PROJECT_TALK ) );
+ // hardcoded namespaces (see above function) are capitalized:
+ $this->assertTrue( MWNamespace::isCapitalized( NS_SPECIAL ) );
+ $this->assertTrue( MWNamespace::isCapitalized( NS_USER ) );
+ $this->assertTrue( MWNamespace::isCapitalized( NS_MEDIAWIKI ) );
+
+ // Hardcoded namespaces remains capitalized
+ $wgCapitalLinkOverrides[NS_SPECIAL] = false;
+ $wgCapitalLinkOverrides[NS_USER] = false;
+ $wgCapitalLinkOverrides[NS_MEDIAWIKI] = false;
+ $this->assertTrue( MWNamespace::isCapitalized( NS_SPECIAL ) );
+ $this->assertTrue( MWNamespace::isCapitalized( NS_USER ) );
+ $this->assertTrue( MWNamespace::isCapitalized( NS_MEDIAWIKI ) );
+
+ $wgCapitalLinkOverrides = $savedGlobal;
+ $wgCapitalLinkOverrides[NS_PROJECT] = false;
+ $this->assertFalse( MWNamespace::isCapitalized( NS_PROJECT ) );
+ $wgCapitalLinkOverrides[NS_PROJECT] = true ;
+ $this->assertTrue( MWNamespace::isCapitalized( NS_PROJECT ) );
+ unset( $wgCapitalLinkOverrides[NS_PROJECT] );
+ $this->assertTrue( MWNamespace::isCapitalized( NS_PROJECT ) );
+
+ // reset global state:
+ $wgCapitalLinkOverrides = $savedGlobal;
+ }
+
+ public function testHasGenderDistinction() {
+ // Namespaces with gender distinctions
+ $this->assertTrue( MWNamespace::hasGenderDistinction( NS_USER ) );
+ $this->assertTrue( MWNamespace::hasGenderDistinction( NS_USER_TALK ) );
+
+ // Other ones, "genderless"
+ $this->assertFalse( MWNamespace::hasGenderDistinction( NS_MEDIA ) );
+ $this->assertFalse( MWNamespace::hasGenderDistinction( NS_SPECIAL ) );
+ $this->assertFalse( MWNamespace::hasGenderDistinction( NS_MAIN ) );
+ $this->assertFalse( MWNamespace::hasGenderDistinction( NS_TALK ) );
+
+ }
+}
+
diff --git a/tests/phpunit/includes/MessageTest.php b/tests/phpunit/includes/MessageTest.php
new file mode 100644
index 00000000..45c02bbe
--- /dev/null
+++ b/tests/phpunit/includes/MessageTest.php
@@ -0,0 +1,62 @@
+<?php
+
+class MessageTest extends MediaWikiLangTestCase {
+
+ function testExists() {
+ $this->assertTrue( wfMessage( 'mainpage' )->exists() );
+ $this->assertTrue( wfMessage( 'mainpage' )->params( array() )->exists() );
+ $this->assertTrue( wfMessage( 'mainpage' )->rawParams( 'foo', 123 )->exists() );
+ $this->assertFalse( wfMessage( 'i-dont-exist-evar' )->exists() );
+ $this->assertFalse( wfMessage( 'i-dont-exist-evar' )->params( array() )->exists() );
+ $this->assertFalse( wfMessage( 'i-dont-exist-evar' )->rawParams( 'foo', 123 )->exists() );
+ }
+
+ function testKey() {
+ $this->assertInstanceOf( 'Message', wfMessage( 'mainpage' ) );
+ $this->assertInstanceOf( 'Message', wfMessage( 'i-dont-exist-evar' ) );
+ $this->assertEquals( 'Main Page', wfMessage( 'mainpage' )->text() );
+ $this->assertEquals( '&lt;i-dont-exist-evar&gt;', wfMessage( 'i-dont-exist-evar' )->text() );
+ }
+
+ function testInLanguage() {
+ $this->assertEquals( 'Main Page', wfMessage( 'mainpage' )->inLanguage( 'en' )->text() );
+ $this->assertEquals( 'Ð—Ð°Ð³Ð»Ð°Ð²Ð½Ð°Ñ Ñтраница', wfMessage( 'mainpage' )->inLanguage( 'ru' )->text() );
+ $this->assertEquals( 'Main Page', wfMessage( 'mainpage' )->inLanguage( Language::factory( 'en' ) )->text() );
+ $this->assertEquals( 'Ð—Ð°Ð³Ð»Ð°Ð²Ð½Ð°Ñ Ñтраница', wfMessage( 'mainpage' )->inLanguage( Language::factory( 'ru' ) )->text() );
+ }
+
+ function testMessageParams() {
+ $this->assertEquals( 'Return to $1.', wfMessage( 'returnto' )->text() );
+ $this->assertEquals( 'Return to $1.', wfMessage( 'returnto', array() )->text() );
+ $this->assertEquals( 'You have foo (bar).', wfMessage( 'youhavenewmessages', 'foo', 'bar' )->text() );
+ $this->assertEquals( 'You have foo (bar).', wfMessage( 'youhavenewmessages', array( 'foo', 'bar' ) )->text() );
+ }
+
+ function testMessageParamSubstitution() {
+ $this->assertEquals( '(Ð—Ð°Ð³Ð»Ð°Ð²Ð½Ð°Ñ Ñтраница)', wfMessage( 'parentheses', 'Ð—Ð°Ð³Ð»Ð°Ð²Ð½Ð°Ñ Ñтраница' )->plain() );
+ $this->assertEquals( '(Ð—Ð°Ð³Ð»Ð°Ð²Ð½Ð°Ñ Ñтраница $1)', wfMessage( 'parentheses', 'Ð—Ð°Ð³Ð»Ð°Ð²Ð½Ð°Ñ Ñтраница $1' )->plain() );
+ $this->assertEquals( '(Ð—Ð°Ð³Ð»Ð°Ð²Ð½Ð°Ñ Ñтраница)', wfMessage( 'parentheses' )->rawParams( 'Ð—Ð°Ð³Ð»Ð°Ð²Ð½Ð°Ñ Ñтраница' )->plain() );
+ $this->assertEquals( '(Ð—Ð°Ð³Ð»Ð°Ð²Ð½Ð°Ñ Ñтраница $1)', wfMessage( 'parentheses' )->rawParams( 'Ð—Ð°Ð³Ð»Ð°Ð²Ð½Ð°Ñ Ñтраница $1' )->plain() );
+ }
+
+ function testInContentLanguage() {
+ global $wgLang, $wgForceUIMsgAsContentMsg;
+ $oldLang = $wgLang;
+ $wgLang = Language::factory( 'fr' );
+
+ $this->assertEquals( 'Main Page', wfMessage( 'mainpage' )->inContentLanguage()->plain(), 'ForceUIMsg disabled' );
+ $wgForceUIMsgAsContentMsg['testInContentLanguage'] = 'mainpage';
+ $this->assertEquals( 'Accueil', wfMessage( 'mainpage' )->inContentLanguage()->plain(), 'ForceUIMsg enabled' );
+
+ /* Restore globals */
+ $wgLang = $oldLang;
+ unset( $wgForceUIMsgAsContentMsg['testInContentLanguage'] );
+ }
+
+ /**
+ * @expectedException MWException
+ */
+ function testInLanguageThrows() {
+ wfMessage( 'foo' )->inLanguage( 123 );
+ }
+}
diff --git a/tests/phpunit/includes/ParserOptionsTest.php b/tests/phpunit/includes/ParserOptionsTest.php
new file mode 100644
index 00000000..58c89146
--- /dev/null
+++ b/tests/phpunit/includes/ParserOptionsTest.php
@@ -0,0 +1,36 @@
+<?php
+
+class ParserOptionsTest extends MediaWikiTestCase {
+
+ private $popts;
+ private $pcache;
+
+ function setUp() {
+ ParserTest::setUp(); //reuse setup from parser tests
+ global $wgContLang, $wgUser, $wgLanguageCode;
+ $wgContLang = Language::factory( $wgLanguageCode );
+ $this->popts = new ParserOptions( $wgUser );
+ $this->pcache = ParserCache::singleton();
+ }
+
+ function tearDown() {
+ parent::tearDown();
+ }
+
+ /**
+ * ParserOptions::optionsHash was not giving consistent results when $wgUseDynamicDates was set
+ * @group Database
+ */
+ function testGetParserCacheKeyWithDynamicDates() {
+ global $wgUseDynamicDates;
+ $wgUseDynamicDates = true;
+
+ $title = Title::newFromText( "Some test article" );
+ $article = new Article( $title );
+
+ $pcacheKeyBefore = $this->pcache->getKey( $article, $this->popts );
+ $this->assertNotNull( $this->popts->getDateFormat() );
+ $pcacheKeyAfter = $this->pcache->getKey( $article, $this->popts );
+ $this->assertEquals( $pcacheKeyBefore, $pcacheKeyAfter );
+ }
+}
diff --git a/tests/phpunit/includes/Providers.php b/tests/phpunit/includes/Providers.php
new file mode 100644
index 00000000..02898673
--- /dev/null
+++ b/tests/phpunit/includes/Providers.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Generic providers for the MediaWiki PHPUnit test suite
+ *
+ * @author Ashar Voultoiz
+ * @copyright Copyright © 2011, Ashar Voultoiz
+ * @file
+ */
+
+/** */
+class MediaWikiProvide {
+
+ /* provide an array of numbers from 1 up to @param $num */
+ private static function createProviderUpTo( $num ) {
+ $ret = array();
+ for( $i=1; $i<=$num;$i++ ) {
+ $ret[] = array( $i );
+ }
+ return $ret;
+ }
+
+ /* array of months numbers (as an integer) */
+ public static function Months() {
+ return self::createProviderUpTo( 12 );
+ }
+
+ /* array of days numbers (as an integer) */
+ public static function Days() {
+ return self::createProviderUpTo( 31 );
+ }
+
+ public static function DaysMonths() {
+ $ret = array();
+
+ $months = self::Months();
+ $days = self::Days();
+ foreach( $months as $month) {
+ foreach( $days as $day ) {
+ $ret[] = array( $day[0], $month[0] );
+ }
+ }
+ return $ret;
+ }
+}
diff --git a/tests/phpunit/includes/ResourceLoaderTest.php b/tests/phpunit/includes/ResourceLoaderTest.php
new file mode 100644
index 00000000..30a69c5e
--- /dev/null
+++ b/tests/phpunit/includes/ResourceLoaderTest.php
@@ -0,0 +1,91 @@
+<?php
+
+class ResourceLoaderTest extends PHPUnit_Framework_TestCase {
+
+ protected static $resourceLoaderRegisterModulesHook;
+
+ /* Hook Methods */
+
+ /**
+ * ResourceLoaderRegisterModules hook
+ */
+ public static function resourceLoaderRegisterModules( &$resourceLoader ) {
+ self::$resourceLoaderRegisterModulesHook = true;
+ return true;
+ }
+
+ /* Provider Methods */
+ public function provideValidModules() {
+ return array(
+ array( 'TEST.validModule1', new ResourceLoaderTestModule() ),
+ );
+ }
+
+ /* Test Methods */
+
+ /**
+ * Ensures that the ResourceLoaderRegisterModules hook is called when a new ResourceLoader object is constructed
+ * @covers ResourceLoader::__construct
+ */
+ public function testCreatingNewResourceLoaderCallsRegistrationHook() {
+ self::$resourceLoaderRegisterModulesHook = false;
+ $resourceLoader = new ResourceLoader();
+ $this->assertTrue( self::$resourceLoaderRegisterModulesHook );
+ return $resourceLoader;
+ }
+
+ /**
+ * @dataProvider provideValidModules
+ * @depends testCreatingNewResourceLoaderCallsRegistrationHook
+ * @covers ResourceLoader::register
+ * @covers ResourceLoader::getModule
+ */
+ public function testRegisteredValidModulesAreAccessible(
+ $name, ResourceLoaderModule $module, ResourceLoader $resourceLoader
+ ) {
+ $resourceLoader->register( $name, $module );
+ $this->assertEquals( $module, $resourceLoader->getModule( $name ) );
+ }
+
+ /**
+ * @dataProvider providePackedModules
+ */
+ public function testMakePackedModulesString( $desc, $modules, $packed ) {
+ $this->assertEquals( $packed, ResourceLoader::makePackedModulesString( $modules ), $desc );
+ }
+
+ /**
+ * @dataProvider providePackedModules
+ */
+ public function testexpandModuleNames( $desc, $modules, $packed ) {
+ $this->assertEquals( $modules, ResourceLoaderContext::expandModuleNames( $packed ), $desc );
+ }
+
+ public function providePackedModules() {
+ return array(
+ array(
+ 'Example from makePackedModulesString doc comment',
+ array( 'foo.bar', 'foo.baz', 'bar.baz', 'bar.quux' ),
+ 'foo.bar,baz|bar.baz,quux',
+ ),
+ array(
+ 'Example from expandModuleNames doc comment',
+ array( 'jquery.foo', 'jquery.bar', 'jquery.ui.baz', 'jquery.ui.quux' ),
+ 'jquery.foo,bar|jquery.ui.baz,quux',
+ ),
+ array(
+ 'Regression fixed in r88706 with dotless names',
+ array( 'foo', 'bar', 'baz' ),
+ 'foo,bar,baz',
+ )
+ );
+ }
+}
+
+/* Stubs */
+
+class ResourceLoaderTestModule extends ResourceLoaderModule { }
+
+/* Hooks */
+global $wgHooks;
+$wgHooks['ResourceLoaderRegisterModules'][] = 'ResourceLoaderTest::resourceLoaderRegisterModules';
diff --git a/tests/phpunit/includes/RevisionTest.php b/tests/phpunit/includes/RevisionTest.php
new file mode 100644
index 00000000..d7654db9
--- /dev/null
+++ b/tests/phpunit/includes/RevisionTest.php
@@ -0,0 +1,125 @@
+<?php
+
+class RevisionTest extends MediaWikiTestCase {
+ var $saveGlobals = array();
+
+ function setUp() {
+ global $wgContLang;
+ $wgContLang = Language::factory( 'en' );
+ $globalSet = array(
+ 'wgLegacyEncoding' => false,
+ 'wgCompressRevisions' => false,
+ );
+ foreach ( $globalSet as $var => $data ) {
+ $this->saveGlobals[$var] = $GLOBALS[$var];
+ $GLOBALS[$var] = $data;
+ }
+ }
+
+ function tearDown() {
+ foreach ( $this->saveGlobals as $var => $data ) {
+ $GLOBALS[$var] = $data;
+ }
+ }
+
+ function testGetRevisionText() {
+ $row = new stdClass;
+ $row->old_flags = '';
+ $row->old_text = 'This is a bunch of revision text.';
+ $this->assertEquals(
+ 'This is a bunch of revision text.',
+ Revision::getRevisionText( $row ) );
+ }
+
+ function testGetRevisionTextGzip() {
+ if ( !function_exists( 'gzdeflate' ) ) {
+ $this->markTestSkipped( 'Gzip compression is not enabled (requires zlib).' );
+ } else {
+ $row = new stdClass;
+ $row->old_flags = 'gzip';
+ $row->old_text = gzdeflate( 'This is a bunch of revision text.' );
+ $this->assertEquals(
+ 'This is a bunch of revision text.',
+ Revision::getRevisionText( $row ) );
+ }
+ }
+
+ function testGetRevisionTextUtf8Native() {
+ $row = new stdClass;
+ $row->old_flags = 'utf-8';
+ $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
+ $GLOBALS['wgLegacyEncoding'] = 'iso-8859-1';
+ $this->assertEquals(
+ "Wiki est l'\xc3\xa9cole superieur !",
+ Revision::getRevisionText( $row ) );
+ }
+
+ function testGetRevisionTextUtf8Legacy() {
+ $row = new stdClass;
+ $row->old_flags = '';
+ $row->old_text = "Wiki est l'\xe9cole superieur !";
+ $GLOBALS['wgLegacyEncoding'] = 'iso-8859-1';
+ $this->assertEquals(
+ "Wiki est l'\xc3\xa9cole superieur !",
+ Revision::getRevisionText( $row ) );
+ }
+
+ function testGetRevisionTextUtf8NativeGzip() {
+ if ( !function_exists( 'gzdeflate' ) ) {
+ $this->markTestSkipped( 'Gzip compression is not enabled (requires zlib).' );
+ } else {
+ $row = new stdClass;
+ $row->old_flags = 'gzip,utf-8';
+ $row->old_text = gzdeflate( "Wiki est l'\xc3\xa9cole superieur !" );
+ $GLOBALS['wgLegacyEncoding'] = 'iso-8859-1';
+ $this->assertEquals(
+ "Wiki est l'\xc3\xa9cole superieur !",
+ Revision::getRevisionText( $row ) );
+ }
+ }
+
+ function testGetRevisionTextUtf8LegacyGzip() {
+ if ( !function_exists( 'gzdeflate' ) ) {
+ $this->markTestSkipped( 'Gzip compression is not enabled (requires zlib).' );
+ } else {
+ $row = new stdClass;
+ $row->old_flags = 'gzip';
+ $row->old_text = gzdeflate( "Wiki est l'\xe9cole superieur !" );
+ $GLOBALS['wgLegacyEncoding'] = 'iso-8859-1';
+ $this->assertEquals(
+ "Wiki est l'\xc3\xa9cole superieur !",
+ Revision::getRevisionText( $row ) );
+ }
+ }
+
+ function testCompressRevisionTextUtf8() {
+ $row = new stdClass;
+ $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
+ $row->old_flags = Revision::compressRevisionText( $row->old_text );
+ $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ),
+ "Flags should contain 'utf-8'" );
+ $this->assertFalse( false !== strpos( $row->old_flags, 'gzip' ),
+ "Flags should not contain 'gzip'" );
+ $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
+ $row->old_text, "Direct check" );
+ $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
+ Revision::getRevisionText( $row ), "getRevisionText" );
+ }
+
+ function testCompressRevisionTextUtf8Gzip() {
+ $GLOBALS['wgCompressRevisions'] = true;
+ $row = new stdClass;
+ $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
+ $row->old_flags = Revision::compressRevisionText( $row->old_text );
+ $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ),
+ "Flags should contain 'utf-8'" );
+ $this->assertTrue( false !== strpos( $row->old_flags, 'gzip' ),
+ "Flags should contain 'gzip'" );
+ $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
+ gzinflate( $row->old_text ), "Direct check" );
+ $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
+ Revision::getRevisionText( $row ), "getRevisionText" );
+ }
+}
+
+
diff --git a/tests/phpunit/includes/SampleTest.php b/tests/phpunit/includes/SampleTest.php
new file mode 100644
index 00000000..77a371d5
--- /dev/null
+++ b/tests/phpunit/includes/SampleTest.php
@@ -0,0 +1,98 @@
+<?php
+
+class TestSample extends MediaWikiLangTestCase {
+
+ /**
+ * Anything that needs to happen before your tests should go here.
+ */
+ function setUp() {
+ global $wgContLang;
+ parent::setUp();
+
+ /* For example, we need to set $wgContLang for creating a new Title */
+ $wgContLang = Language::factory( 'en' );
+ }
+
+ /**
+ * Anything cleanup you need to do should go here.
+ */
+ function tearDown() {
+ parent::tearDown();
+ }
+
+ /**
+ * Name tests so that PHPUnit can turn them into sentances when
+ * they run. While MediaWiki isn't strictly an Agile Programming
+ * project, you are encouraged to use the naming described under
+ * "Agile Documentation" at
+ * http://www.phpunit.de/manual/3.4/en/other-uses-for-tests.html
+ */
+ function testTitleObjectStringConversion() {
+ $title = Title::newFromText("text");
+ $this->assertEquals("Text", $title->__toString(), "Title creation");
+ $this->assertEquals("Text", "Text", "Automatic string conversion");
+
+ $title = Title::newFromText("text", NS_MEDIA);
+ $this->assertEquals("Media:Text", $title->__toString(), "Title creation with namespace");
+
+ }
+
+ /**
+ * If you want to run a the same test with a variety of data. use a data provider.
+ * see: http://www.phpunit.de/manual/3.4/en/writing-tests-for-phpunit.html
+ */
+ public function provideTitles() {
+ return array(
+ array( 'Text', NS_MEDIA, 'Media:Text' ),
+ array( 'Text', null, 'Text' ),
+ array( 'text', null, 'Text' ),
+ array( 'Text', NS_USER, 'User:Text' ),
+ array( 'Photo.jpg', NS_IMAGE, 'File:Photo.jpg' )
+ );
+ }
+
+ /**
+ * @dataProvider provideTitles
+ * See http://www.phpunit.de/manual/3.4/en/appendixes.annotations.html#appendixes.annotations.dataProvider
+ */
+ public function testCreateBasicListOfTitles($titleName, $ns, $text) {
+ $title = Title::newFromText($titleName, $ns);
+ $this->assertEquals($text, "$title", "see if '$titleName' matches '$text'");
+ }
+
+ public function testSetUpMainPageTitleForNextTest() {
+ $title = Title::newMainPage();
+ $this->assertEquals("Main Page", "$title", "Test initial creation of a title");
+
+ return $title;
+ }
+
+ /**
+ * Instead of putting a bunch of tests in a single test method,
+ * you should put only one or two tests in each test method. This
+ * way, the test method names can remain descriptive.
+ *
+ * If you want to make tests depend on data created in another
+ * method, you can create dependencies feed whatever you return
+ * from the dependant method (e.g. testInitialCreation in this
+ * example) as arguments to the next method (e.g. $title in
+ * testTitleDepends is whatever testInitialCreatiion returned.)
+ */
+ /**
+ * @depends testSetUpMainPageTitleForNextTest
+ * See http://www.phpunit.de/manual/3.4/en/appendixes.annotations.html#appendixes.annotations.depends
+ */
+ public function testCheckMainPageTitleIsConsideredLocal( $title ) {
+ $this->assertTrue( $title->isLocal() );
+ }
+
+ /**
+ * @expectedException MWException object
+ * See http://www.phpunit.de/manual/3.4/en/appendixes.annotations.html#appendixes.annotations.expectedException
+ */
+ function testTitleObjectFromObject() {
+ $title = Title::newFromText( Title::newFromText( "test" ) );
+ $this->assertEquals( "Test", $title->isLocal() );
+ }
+}
+
diff --git a/tests/phpunit/includes/SanitizerTest.php b/tests/phpunit/includes/SanitizerTest.php
new file mode 100644
index 00000000..40d6cf77
--- /dev/null
+++ b/tests/phpunit/includes/SanitizerTest.php
@@ -0,0 +1,113 @@
+<?php
+
+class SanitizerTest extends MediaWikiTestCase {
+
+ function setUp() {
+ AutoLoader::loadClass( 'Sanitizer' );
+ }
+
+ function testDecodeNamedEntities() {
+ $this->assertEquals(
+ "\xc3\xa9cole",
+ Sanitizer::decodeCharReferences( '&eacute;cole' ),
+ 'decode named entities'
+ );
+ }
+
+ function testDecodeNumericEntities() {
+ $this->assertEquals(
+ "\xc4\x88io bonas dans l'\xc3\xa9cole!",
+ Sanitizer::decodeCharReferences( "&#x108;io bonas dans l'&#233;cole!" ),
+ 'decode numeric entities'
+ );
+ }
+
+ function testDecodeMixedEntities() {
+ $this->assertEquals(
+ "\xc4\x88io bonas dans l'\xc3\xa9cole!",
+ Sanitizer::decodeCharReferences( "&#x108;io bonas dans l'&eacute;cole!" ),
+ 'decode mixed numeric/named entities'
+ );
+ }
+
+ function testDecodeMixedComplexEntities() {
+ $this->assertEquals(
+ "\xc4\x88io bonas dans l'\xc3\xa9cole! (mais pas &#x108;io dans l'&eacute;cole)",
+ Sanitizer::decodeCharReferences(
+ "&#x108;io bonas dans l'&eacute;cole! (mais pas &amp;#x108;io dans l'&#38;eacute;cole)"
+ ),
+ 'decode mixed complex entities'
+ );
+ }
+
+ function testInvalidAmpersand() {
+ $this->assertEquals(
+ 'a & b',
+ Sanitizer::decodeCharReferences( 'a & b' ),
+ 'Invalid ampersand'
+ );
+ }
+
+ function testInvalidEntities() {
+ $this->assertEquals(
+ '&foo;',
+ Sanitizer::decodeCharReferences( '&foo;' ),
+ 'Invalid named entity'
+ );
+ }
+
+ function testInvalidNumberedEntities() {
+ $this->assertEquals( UTF8_REPLACEMENT, Sanitizer::decodeCharReferences( "&#88888888888888;" ), 'Invalid numbered entity' );
+ }
+
+ function testSelfClosingTag() {
+ $GLOBALS['wgUseTidy'] = false;
+ $this->assertEquals(
+ '<div>Hello world</div>',
+ Sanitizer::removeHTMLtags( '<div>Hello world</div />' ),
+ 'Self-closing closing div'
+ );
+ }
+
+ function testDecodeTagAttributes() {
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo=bar' ), array( 'foo' => 'bar' ), 'Unquoted attribute' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( ' foo = bar ' ), array( 'foo' => 'bar' ), 'Spaced attribute' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo="bar"' ), array( 'foo' => 'bar' ), 'Double-quoted attribute' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo=\'bar\'' ), array( 'foo' => 'bar' ), 'Single-quoted attribute' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo=\'bar\' baz="foo"' ), array( 'foo' => 'bar', 'baz' => 'foo' ), 'Several attributes' );
+
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo=\'bar\' baz="foo"' ), array( 'foo' => 'bar', 'baz' => 'foo' ), 'Several attributes' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo=\'bar\' baz="foo"' ), array( 'foo' => 'bar', 'baz' => 'foo' ), 'Several attributes' );
+
+ $this->assertEquals( Sanitizer::decodeTagAttributes( ':foo=\'bar\'' ), array( ':foo' => 'bar' ), 'Leading :' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( '_foo=\'bar\'' ), array( '_foo' => 'bar' ), 'Leading _' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'Foo=\'bar\'' ), array( 'foo' => 'bar' ), 'Leading capital' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'FOO=BAR' ), array( 'foo' => 'BAR' ), 'Attribute keys are normalized to lowercase' );
+
+ # Invalid beginning
+ $this->assertEquals( Sanitizer::decodeTagAttributes( '-foo=bar' ), array(), 'Leading - is forbidden' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( '.foo=bar' ), array(), 'Leading . is forbidden' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo-bar=bar' ), array( 'foo-bar' => 'bar' ), 'A - is allowed inside the attribute' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo-=bar' ), array( 'foo-' => 'bar' ), 'A - is allowed inside the attribute' );
+
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo.bar=baz' ), array( 'foo.bar' => 'baz' ), 'A . is allowed inside the attribute' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo.=baz' ), array( 'foo.' => 'baz' ), 'A . is allowed as last character' );
+
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo6=baz' ), array( 'foo6' => 'baz' ), 'Numbers are allowed' );
+
+ # This bit is more relaxed than XML rules, but some extensions use it, like ProofreadPage (see bug 27539)
+ $this->assertEquals( Sanitizer::decodeTagAttributes( '1foo=baz' ), array( '1foo' => 'baz' ), 'Leading numbers are allowed' );
+
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo$=baz' ), array(), 'Symbols are not allowed' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo@=baz' ), array(), 'Symbols are not allowed' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo~=baz' ), array(), 'Symbols are not allowed' );
+
+
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo=1[#^`*%w/(' ), array( 'foo' => '1[#^`*%w/(' ), 'All kind of characters are allowed as values' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo="1[#^`*%\'w/("' ), array( 'foo' => '1[#^`*%\'w/(' ), 'Double quotes are allowed if quoted by single quotes' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo=\'1[#^`*%"w/(\'' ), array( 'foo' => '1[#^`*%"w/(' ), 'Single quotes are allowed if quoted by double quotes' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo=&amp;&quot;' ), array( 'foo' => '&"' ), 'Special chars can be provided as entities' );
+ $this->assertEquals( Sanitizer::decodeTagAttributes( 'foo=&foobar;' ), array( 'foo' => '&foobar;' ), 'Entity-like items are accepted' );
+ }
+}
+
diff --git a/tests/phpunit/includes/SeleniumConfigurationTest.php b/tests/phpunit/includes/SeleniumConfigurationTest.php
new file mode 100644
index 00000000..750524eb
--- /dev/null
+++ b/tests/phpunit/includes/SeleniumConfigurationTest.php
@@ -0,0 +1,228 @@
+<?php
+
+class SeleniumConfigurationTest extends MediaWikiTestCase {
+
+ /*
+ * The file where the test temporarity stores the selenium config.
+ * This should be cleaned up as part of teardown.
+ */
+ private $tempFileName;
+
+ /*
+ * String containing the a sample selenium settings
+ */
+ private $testConfig0 =
+'
+[SeleniumSettings]
+browsers[firefox] = "*firefox"
+browsers[iexplorer] = "*iexploreproxy"
+browsers[chrome] = "*chrome"
+host = "localhost"
+port = "foobarr"
+wikiUrl = "http://localhost/deployment"
+username = "xxxxxxx"
+userPassword = ""
+testBrowser = "chrome"
+startserver =
+stopserver =
+jUnitLogFile =
+runAgainstGrid = false
+
+[SeleniumTests]
+testSuite[SimpleSeleniumTestSuite] = "tests/selenium/SimpleSeleniumTestSuite.php"
+testSuite[TestSuiteName] = "testSuitePath"
+';
+ /*
+ * Array of expected browsers from $testConfig0
+ */
+ private $testBrowsers0 = array( 'firefox' => '*firefox',
+ 'iexplorer' => '*iexploreproxy',
+ 'chrome' => '*chrome'
+ );
+ /*
+ * Array of expected selenium settings from $testConfig0
+ */
+ private $testSettings0 = array(
+ 'host' => 'localhost',
+ 'port' => 'foobarr',
+ 'wikiUrl' => 'http://localhost/deployment',
+ 'username' => 'xxxxxxx',
+ 'userPassword' => '',
+ 'testBrowser' => 'chrome',
+ 'startserver' => null,
+ 'stopserver' => null,
+ 'seleniumserverexecpath' => null,
+ 'jUnitLogFile' => null,
+ 'runAgainstGrid' => null
+ );
+ /*
+ * Array of expected testSuites from $testConfig0
+ */
+ private $testSuites0 = array(
+ 'SimpleSeleniumTestSuite' => 'tests/selenium/SimpleSeleniumTestSuite.php',
+ 'TestSuiteName' => 'testSuitePath'
+ );
+
+
+ /*
+ * Another sample selenium settings file contents
+ */
+ private $testConfig1 =
+'
+[SeleniumSettings]
+host = "localhost"
+testBrowser = "firefox"
+';
+ /*
+ * Expected browsers from $testConfig1
+ */
+ private $testBrowsers1 = null;
+ /*
+ * Expected selenium settings from $testConfig1
+ */
+ private $testSettings1 = array(
+ 'host' => 'localhost',
+ 'port' => null,
+ 'wikiUrl' => null,
+ 'username' => null,
+ 'userPassword' => null,
+ 'testBrowser' => 'firefox',
+ 'startserver' => null,
+ 'stopserver' => null,
+ 'seleniumserverexecpath' => null,
+ 'jUnitLogFile' => null,
+ 'runAgainstGrid' => null
+ );
+ /*
+ * Expected test suites from $testConfig1
+ */
+ private $testSuites1 = null;
+
+
+ public function setUp() {
+ if ( !defined( 'SELENIUMTEST' ) ) {
+ define( 'SELENIUMTEST', true );
+ }
+ }
+
+ /*
+ * Clean up the temporary file used to store the selenium settings.
+ */
+ public function tearDown() {
+ if ( strlen( $this->tempFileName ) > 0 ) {
+ unlink( $this->tempFileName );
+ unset( $this->tempFileName );
+ }
+ parent::tearDown();
+ }
+
+ /**
+ * @expectedException MWException
+ * @group SeleniumFramework
+ */
+ public function testErrorOnIncorrectConfigFile() {
+ $seleniumSettings = array();
+ $seleniumBrowsers = array();
+ $seleniumTestSuites = array();
+
+ SeleniumConfig::getSeleniumSettings($seleniumSettings,
+ $seleniumBrowsers,
+ $seleniumTestSuites,
+ "Some_fake_settings_file.ini" );
+
+ }
+
+ /**
+ * @expectedException MWException
+ * @group SeleniumFramework
+ */
+ public function testErrorOnMissingConfigFile() {
+ $seleniumSettings = array();
+ $seleniumBrowsers = array();
+ $seleniumTestSuites = array();
+ global $wgSeleniumConfigFile;
+ $wgSeleniumConfigFile = '';
+ SeleniumConfig::getSeleniumSettings($seleniumSettings,
+ $seleniumBrowsers,
+ $seleniumTestSuites);
+ }
+
+ /**
+ * @group SeleniumFramework
+ */
+ public function testUsesGlobalVarForConfigFile() {
+ $seleniumSettings = array();
+ $seleniumBrowsers = array();
+ $seleniumTestSuites = array();
+ global $wgSeleniumConfigFile;
+ $this->writeToTempFile( $this->testConfig0 );
+ $wgSeleniumConfigFile = $this->tempFileName;
+ SeleniumConfig::getSeleniumSettings($seleniumSettings,
+ $seleniumBrowsers,
+ $seleniumTestSuites);
+ $this->assertEquals($seleniumSettings, $this->testSettings0 ,
+ 'The selenium settings should have been read from the file defined in $wgSeleniumConfigFile'
+ );
+ $this->assertEquals($seleniumBrowsers, $this->testBrowsers0,
+ 'The available browsers should have been read from the file defined in $wgSeleniumConfigFile'
+ );
+ $this->assertEquals($seleniumTestSuites, $this->testSuites0,
+ 'The test suites should have been read from the file defined in $wgSeleniumConfigFile'
+ );
+ }
+
+ /**
+ * @group SeleniumFramework
+ * @dataProvider sampleConfigs
+ */
+ public function testgetSeleniumSettings($sampleConfig, $expectedSettings, $expectedBrowsers, $expectedSuites ) {
+ $this->writeToTempFile( $sampleConfig );
+ $seleniumSettings = array();
+ $seleniumBrowsers = array();
+ $seleniumTestSuites = null;
+
+ SeleniumConfig::getSeleniumSettings($seleniumSettings,
+ $seleniumBrowsers,
+ $seleniumTestSuites,
+ $this->tempFileName );
+
+ $this->assertEquals($seleniumSettings, $expectedSettings,
+ "The selenium settings for the following test configuration was not retrieved correctly" . $sampleConfig
+ );
+ $this->assertEquals($seleniumBrowsers, $expectedBrowsers,
+ "The available browsers for the following test configuration was not retrieved correctly" . $sampleConfig
+ );
+ $this->assertEquals($seleniumTestSuites, $expectedSuites,
+ "The test suites for the following test configuration was not retrieved correctly" . $sampleConfig
+ );
+
+
+ }
+
+ /*
+ * create a temp file and write text to it.
+ * @param $testToWrite the text to write to the temp file
+ */
+ private function writeToTempFile($textToWrite) {
+ $this->tempFileName = tempnam(sys_get_temp_dir(), 'test_settings.');
+ $tempFile = fopen( $this->tempFileName, "w" );
+ fwrite($tempFile , $textToWrite);
+ fclose($tempFile);
+ }
+
+ /*
+ * Returns an array containing:
+ * The contents of the selenium cingiguration ini file
+ * The expected selenium configuration array that getSeleniumSettings should return
+ * The expected available browsers array that getSeleniumSettings should return
+ * The expected test suites arrya that getSeleniumSettings should return
+ */
+ public function sampleConfigs() {
+ return array(
+ array($this->testConfig0, $this->testSettings0, $this->testBrowsers0, $this->testSuites0 ),
+ array($this->testConfig1, $this->testSettings1, $this->testBrowsers1, $this->testSuites1 )
+ );
+ }
+
+
+}
diff --git a/tests/phpunit/includes/SiteConfigurationTest.php b/tests/phpunit/includes/SiteConfigurationTest.php
new file mode 100644
index 00000000..57d3532a
--- /dev/null
+++ b/tests/phpunit/includes/SiteConfigurationTest.php
@@ -0,0 +1,311 @@
+<?php
+
+function getSiteParams( $conf, $wiki ) {
+ $site = null;
+ $lang = null;
+ foreach ( $conf->suffixes as $suffix ) {
+ if ( substr( $wiki, -strlen( $suffix ) ) == $suffix ) {
+ $site = $suffix;
+ $lang = substr( $wiki, 0, -strlen( $suffix ) );
+ break;
+ }
+ }
+ return array(
+ 'suffix' => $site,
+ 'lang' => $lang,
+ 'params' => array(
+ 'lang' => $lang,
+ 'site' => $site,
+ 'wiki' => $wiki,
+ ),
+ 'tags' => array( 'tag' ),
+ );
+}
+
+class SiteConfigurationTest extends MediaWikiTestCase {
+ var $mConf;
+
+ function setUp() {
+ $this->mConf = new SiteConfiguration;
+
+ $this->mConf->suffixes = array( 'wiki' );
+ $this->mConf->wikis = array( 'enwiki', 'dewiki', 'frwiki' );
+ $this->mConf->settings = array(
+ 'simple' => array(
+ 'wiki' => 'wiki',
+ 'tag' => 'tag',
+ 'enwiki' => 'enwiki',
+ 'dewiki' => 'dewiki',
+ 'frwiki' => 'frwiki',
+ ),
+
+ 'fallback' => array(
+ 'default' => 'default',
+ 'wiki' => 'wiki',
+ 'tag' => 'tag',
+ ),
+
+ 'params' => array(
+ 'default' => '$lang $site $wiki',
+ ),
+
+ '+global' => array(
+ 'wiki' => array(
+ 'wiki' => 'wiki',
+ ),
+ 'tag' => array(
+ 'tag' => 'tag',
+ ),
+ 'enwiki' => array(
+ 'enwiki' => 'enwiki',
+ ),
+ 'dewiki' => array(
+ 'dewiki' => 'dewiki',
+ ),
+ 'frwiki' => array(
+ 'frwiki' => 'frwiki',
+ ),
+ ),
+
+ 'merge' => array(
+ '+wiki' => array(
+ 'wiki' => 'wiki',
+ ),
+ '+tag' => array(
+ 'tag' => 'tag',
+ ),
+ 'default' => array(
+ 'default' => 'default',
+ ),
+ '+enwiki' => array(
+ 'enwiki' => 'enwiki',
+ ),
+ '+dewiki' => array(
+ 'dewiki' => 'dewiki',
+ ),
+ '+frwiki' => array(
+ 'frwiki' => 'frwiki',
+ ),
+ ),
+ );
+
+ $GLOBALS['global'] = array( 'global' => 'global' );
+ }
+
+
+ function testSiteFromDb() {
+ $this->assertEquals(
+ array( 'wikipedia', 'en' ),
+ $this->mConf->siteFromDB( 'enwiki' ),
+ 'siteFromDB()'
+ );
+ $this->assertEquals(
+ array( 'wikipedia', '' ),
+ $this->mConf->siteFromDB( 'wiki' ),
+ 'siteFromDB() on a suffix'
+ );
+ $this->assertEquals(
+ array( null, null ),
+ $this->mConf->siteFromDB( 'wikien' ),
+ 'siteFromDB() on a non-existing wiki'
+ );
+
+ $this->mConf->suffixes = array( 'wiki', '' );
+ $this->assertEquals(
+ array( '', 'wikien' ),
+ $this->mConf->siteFromDB( 'wikien' ),
+ 'siteFromDB() on a non-existing wiki (2)'
+ );
+ }
+
+ function testGetLocalDatabases() {
+ $this->assertEquals(
+ array( 'enwiki', 'dewiki', 'frwiki' ),
+ $this->mConf->getLocalDatabases(),
+ 'getLocalDatabases()'
+ );
+ }
+
+ function testGetConfVariables() {
+ $this->assertEquals(
+ 'enwiki',
+ $this->mConf->get( 'simple', 'enwiki', 'wiki' ),
+ 'get(): simple setting on an existing wiki'
+ );
+ $this->assertEquals(
+ 'dewiki',
+ $this->mConf->get( 'simple', 'dewiki', 'wiki' ),
+ 'get(): simple setting on an existing wiki (2)'
+ );
+ $this->assertEquals(
+ 'frwiki',
+ $this->mConf->get( 'simple', 'frwiki', 'wiki' ),
+ 'get(): simple setting on an existing wiki (3)'
+ );
+ $this->assertEquals(
+ 'wiki',
+ $this->mConf->get( 'simple', 'wiki', 'wiki' ),
+ 'get(): simple setting on an suffix'
+ );
+ $this->assertEquals(
+ 'wiki',
+ $this->mConf->get( 'simple', 'eswiki', 'wiki' ),
+ 'get(): simple setting on an non-existing wiki'
+ );
+
+ $this->assertEquals(
+ 'wiki',
+ $this->mConf->get( 'fallback', 'enwiki', 'wiki' ),
+ 'get(): fallback setting on an existing wiki'
+ );
+ $this->assertEquals(
+ 'tag',
+ $this->mConf->get( 'fallback', 'dewiki', 'wiki', array(), array( 'tag' ) ),
+ 'get(): fallback setting on an existing wiki (with wiki tag)'
+ );
+ $this->assertEquals(
+ 'wiki',
+ $this->mConf->get( 'fallback', 'wiki', 'wiki' ),
+ 'get(): fallback setting on an suffix'
+ );
+ $this->assertEquals(
+ 'wiki',
+ $this->mConf->get( 'fallback', 'wiki', 'wiki', array(), array( 'tag' ) ),
+ 'get(): fallback setting on an suffix (with wiki tag)'
+ );
+ $this->assertEquals(
+ 'wiki',
+ $this->mConf->get( 'fallback', 'eswiki', 'wiki' ),
+ 'get(): fallback setting on an non-existing wiki'
+ );
+ $this->assertEquals(
+ 'tag',
+ $this->mConf->get( 'fallback', 'eswiki', 'wiki', array(), array( 'tag' ) ),
+ 'get(): fallback setting on an non-existing wiki (with wiki tag)'
+ );
+
+ $common = array( 'wiki' => 'wiki', 'default' => 'default' );
+ $commonTag = array( 'tag' => 'tag', 'wiki' => 'wiki', 'default' => 'default' );
+ $this->assertEquals(
+ array( 'enwiki' => 'enwiki' ) + $common,
+ $this->mConf->get( 'merge', 'enwiki', 'wiki' ),
+ 'get(): merging setting on an existing wiki'
+ );
+ $this->assertEquals(
+ array( 'enwiki' => 'enwiki' ) + $commonTag,
+ $this->mConf->get( 'merge', 'enwiki', 'wiki', array(), array( 'tag' ) ),
+ 'get(): merging setting on an existing wiki (with tag)'
+ );
+ $this->assertEquals(
+ array( 'dewiki' => 'dewiki' ) + $common,
+ $this->mConf->get( 'merge', 'dewiki', 'wiki' ),
+ 'get(): merging setting on an existing wiki (2)'
+ );
+ $this->assertEquals(
+ array( 'dewiki' => 'dewiki' ) + $commonTag,
+ $this->mConf->get( 'merge', 'dewiki', 'wiki', array(), array( 'tag' ) ),
+ 'get(): merging setting on an existing wiki (2) (with tag)'
+ );
+ $this->assertEquals(
+ array( 'frwiki' => 'frwiki' ) + $common,
+ $this->mConf->get( 'merge', 'frwiki', 'wiki' ),
+ 'get(): merging setting on an existing wiki (3)'
+ );
+ $this->assertEquals(
+ array( 'frwiki' => 'frwiki' ) + $commonTag,
+ $this->mConf->get( 'merge', 'frwiki', 'wiki', array(), array( 'tag' ) ),
+ 'get(): merging setting on an existing wiki (3) (with tag)'
+ );
+ $this->assertEquals(
+ array( 'wiki' => 'wiki' ) + $common,
+ $this->mConf->get( 'merge', 'wiki', 'wiki' ),
+ 'get(): merging setting on an suffix'
+ );
+ $this->assertEquals(
+ array( 'wiki' => 'wiki' ) + $commonTag,
+ $this->mConf->get( 'merge', 'wiki', 'wiki', array(), array( 'tag' ) ),
+ 'get(): merging setting on an suffix (with tag)'
+ );
+ $this->assertEquals(
+ $common,
+ $this->mConf->get( 'merge', 'eswiki', 'wiki' ),
+ 'get(): merging setting on an non-existing wiki'
+ );
+ $this->assertEquals(
+ $commonTag,
+ $this->mConf->get( 'merge', 'eswiki', 'wiki', array(), array( 'tag' ) ),
+ 'get(): merging setting on an non-existing wiki (with tag)'
+ );
+ }
+
+ function testSiteFromDbWithCallback() {
+ $this->mConf->siteParamsCallback = 'getSiteParams';
+
+ $this->assertEquals(
+ array( 'wiki', 'en' ),
+ $this->mConf->siteFromDB( 'enwiki' ),
+ 'siteFromDB() with callback'
+ );
+ $this->assertEquals(
+ array( 'wiki', '' ),
+ $this->mConf->siteFromDB( 'wiki' ),
+ 'siteFromDB() with callback on a suffix'
+ );
+ $this->assertEquals(
+ array( null, null ),
+ $this->mConf->siteFromDB( 'wikien' ),
+ 'siteFromDB() with callback on a non-existing wiki'
+ );
+ }
+
+ function testParameterReplacement() {
+ $this->mConf->siteParamsCallback = 'getSiteParams';
+
+ $this->assertEquals(
+ 'en wiki enwiki',
+ $this->mConf->get( 'params', 'enwiki', 'wiki' ),
+ 'get(): parameter replacement on an existing wiki'
+ );
+ $this->assertEquals(
+ 'de wiki dewiki',
+ $this->mConf->get( 'params', 'dewiki', 'wiki' ),
+ 'get(): parameter replacement on an existing wiki (2)'
+ );
+ $this->assertEquals(
+ 'fr wiki frwiki',
+ $this->mConf->get( 'params', 'frwiki', 'wiki' ),
+ 'get(): parameter replacement on an existing wiki (3)'
+ );
+ $this->assertEquals(
+ ' wiki wiki',
+ $this->mConf->get( 'params', 'wiki', 'wiki' ),
+ 'get(): parameter replacement on an suffix'
+ );
+ $this->assertEquals(
+ 'es wiki eswiki',
+ $this->mConf->get( 'params', 'eswiki', 'wiki' ),
+ 'get(): parameter replacement on an non-existing wiki'
+ );
+ }
+
+ function testGetAllGlobals() {
+ $this->mConf->siteParamsCallback = 'getSiteParams';
+
+ $getall = array(
+ 'simple' => 'enwiki',
+ 'fallback' => 'tag',
+ 'params' => 'en wiki enwiki',
+ 'global' => array( 'enwiki' => 'enwiki' ) + $GLOBALS['global'],
+ 'merge' => array( 'enwiki' => 'enwiki', 'tag' => 'tag', 'wiki' => 'wiki', 'default' => 'default' ),
+ );
+ $this->assertEquals( $getall, $this->mConf->getAll( 'enwiki' ), 'getAll()' );
+
+ $this->mConf->extractAllGlobals( 'enwiki', 'wiki' );
+
+ $this->assertEquals( $getall['simple'], $GLOBALS['simple'], 'extractAllGlobals(): simple setting' );
+ $this->assertEquals( $getall['fallback'], $GLOBALS['fallback'], 'extractAllGlobals(): fallback setting' );
+ $this->assertEquals( $getall['params'], $GLOBALS['params'], 'extractAllGlobals(): parameter replacement' );
+ $this->assertEquals( $getall['global'], $GLOBALS['global'], 'extractAllGlobals(): merging with global' );
+ $this->assertEquals( $getall['merge'], $GLOBALS['merge'], 'extractAllGlobals(): merging setting' );
+ }
+}
diff --git a/tests/phpunit/includes/TimeAdjustTest.php b/tests/phpunit/includes/TimeAdjustTest.php
new file mode 100644
index 00000000..cd027c5b
--- /dev/null
+++ b/tests/phpunit/includes/TimeAdjustTest.php
@@ -0,0 +1,51 @@
+<?php
+
+class TimeAdjustTest extends MediaWikiLangTestCase {
+ static $offset;
+
+ public function setUp() {
+ parent::setUp();
+ global $wgLocalTZoffset;
+ self::$offset = $wgLocalTZoffset;
+
+ $this->iniSet( 'precision', 15 );
+ }
+
+ public function tearDown() {
+ global $wgLocalTZoffset;
+ $wgLocalTZoffset = self::$offset;
+ parent::tearDown();
+ }
+
+ # Test offset usage for a given language::userAdjust
+ function testUserAdjust() {
+ global $wgLocalTZoffset, $wgContLang;
+
+ $wgContLang = $en = Language::factory( 'en' );
+
+ #  Collection of parameters for Language_t_Offset.
+ # Format: date to be formatted, localTZoffset value, expected date
+ $userAdjust_tests = array(
+ array( 20061231235959, 0, 20061231235959 ),
+ array( 20061231235959, 5, 20070101000459 ),
+ array( 20061231235959, 15, 20070101001459 ),
+ array( 20061231235959, 60, 20070101005959 ),
+ array( 20061231235959, 90, 20070101012959 ),
+ array( 20061231235959, 120, 20070101015959 ),
+ array( 20061231235959, 540, 20070101085959 ),
+ array( 20061231235959, -5, 20061231235459 ),
+ array( 20061231235959, -30, 20061231232959 ),
+ array( 20061231235959, -60, 20061231225959 ),
+ );
+
+ foreach ( $userAdjust_tests as $data ) {
+ $wgLocalTZoffset = $data[1];
+
+ $this->assertEquals(
+ strval( $data[2] ),
+ strval( $en->userAdjust( $data[0], '' ) ),
+ "User adjust {$data[0]} by {$data[1]} minutes should give {$data[2]}"
+ );
+ }
+ }
+}
diff --git a/tests/phpunit/includes/TitlePermissionTest.php b/tests/phpunit/includes/TitlePermissionTest.php
new file mode 100644
index 00000000..1b179686
--- /dev/null
+++ b/tests/phpunit/includes/TitlePermissionTest.php
@@ -0,0 +1,654 @@
+<?php
+
+/**
+ * @group Database
+ */
+class TitlePermissionTest extends MediaWikiLangTestCase {
+ protected $title;
+ protected $user;
+ protected $anonUser;
+ protected $userUser;
+ protected $altUser;
+ protected $userName;
+ protected $altUserName;
+
+ function setUp() {
+ global $wgLocaltimezone, $wgLocalTZoffset, $wgMemc, $wgContLang, $wgLang;
+ parent::setUp();
+
+ if(!$wgMemc) {
+ $wgMemc = new EmptyBagOStuff;
+ }
+ $wgContLang = $wgLang = Language::factory( 'en' );
+
+ $this->userName = "Useruser";
+ $this->altUserName = "Altuseruser";
+ date_default_timezone_set( $wgLocaltimezone );
+ $wgLocalTZoffset = date( "Z" ) / 60;
+
+ $this->title = Title::makeTitle( NS_MAIN, "Main Page" );
+ if ( !isset( $this->userUser ) || !( $this->userUser instanceOf User ) ) {
+ $this->userUser = User::newFromName( $this->userName );
+
+ if ( !$this->userUser->getID() ) {
+ $this->userUser = User::createNew( $this->userName, array(
+ "email" => "test@example.com",
+ "real_name" => "Test User" ) );
+ $this->userUser->load();
+ }
+
+ $this->altUser = User::newFromName( $this->altUserName );
+ if ( !$this->altUser->getID() ) {
+ $this->altUser = User::createNew( $this->altUserName, array(
+ "email" => "alttest@example.com",
+ "real_name" => "Test User Alt" ) );
+ $this->altUser->load();
+ }
+
+ $this->anonUser = User::newFromId( 0 );
+
+ $this->user = $this->userUser;
+ }
+ }
+
+ function tearDown() {
+ parent::tearDown();
+ }
+
+ function setUserPerm( $perm ) {
+ if ( is_array( $perm ) ) {
+ $this->user->mRights = $perm;
+ } else {
+ $this->user->mRights = array( $perm );
+ }
+ }
+
+ function setTitle( $ns, $title = "Main_Page" ) {
+ $this->title = Title::makeTitle( $ns, $title );
+ }
+
+ function setUser( $userName = null ) {
+ if ( $userName === 'anon' ) {
+ $this->user = $this->anonUser;
+ } elseif ( $userName === null || $userName === $this->userName ) {
+ $this->user = $this->userUser;
+ } else {
+ $this->user = $this->altUser;
+ }
+
+ global $wgUser;
+ $wgUser = $this->user;
+ }
+
+ function testQuickPermissions() {
+ global $wgContLang;
+ $prefix = $wgContLang->getFormattedNsText( NS_PROJECT );
+
+ $this->setUser( 'anon' );
+ $this->setTitle( NS_TALK );
+ $this->setUserPerm( "createtalk" );
+ $res = $this->title->getUserPermissionsErrors( 'create', $this->user );
+ $this->assertEquals( array(), $res );
+
+ $this->setTitle( NS_TALK );
+ $this->setUserPerm( "createpage" );
+ $res = $this->title->getUserPermissionsErrors( 'create', $this->user );
+ $this->assertEquals( array( array( "nocreatetext" ) ), $res );
+
+ $this->setTitle( NS_TALK );
+ $this->setUserPerm( "" );
+ $res = $this->title->getUserPermissionsErrors( 'create', $this->user );
+ $this->assertEquals( array( array( 'nocreatetext' ) ), $res );
+
+ $this->setTitle( NS_MAIN );
+ $this->setUserPerm( "createpage" );
+ $res = $this->title->getUserPermissionsErrors( 'create', $this->user );
+ $this->assertEquals( array( ), $res );
+
+ $this->setTitle( NS_MAIN );
+ $this->setUserPerm( "createtalk" );
+ $res = $this->title->getUserPermissionsErrors( 'create', $this->user );
+ $this->assertEquals( array( array( 'nocreatetext' ) ), $res );
+
+ $this->setUser( $this->userName );
+ $this->setTitle( NS_TALK );
+ $this->setUserPerm( "createtalk" );
+ $res = $this->title->getUserPermissionsErrors( 'create', $this->user );
+ $this->assertEquals( array( ), $res );
+
+ $this->setTitle( NS_TALK );
+ $this->setUserPerm( "createpage" );
+ $res = $this->title->getUserPermissionsErrors( 'create', $this->user );
+ $this->assertEquals( array( array( 'nocreate-loggedin' ) ), $res );
+
+ $this->setTitle( NS_TALK );
+ $this->setUserPerm( "" );
+ $res = $this->title->getUserPermissionsErrors( 'create', $this->user );
+ $this->assertEquals( array( array( 'nocreate-loggedin' ) ), $res );
+
+ $this->setTitle( NS_MAIN );
+ $this->setUserPerm( "createpage" );
+ $res = $this->title->getUserPermissionsErrors( 'create', $this->user );
+ $this->assertEquals( array( ), $res );
+
+ $this->setTitle( NS_MAIN );
+ $this->setUserPerm( "createtalk" );
+ $res = $this->title->getUserPermissionsErrors( 'create', $this->user );
+ $this->assertEquals( array( array( 'nocreate-loggedin' ) ), $res );
+
+ $this->setTitle( NS_MAIN );
+ $this->setUserPerm( "" );
+ $res = $this->title->getUserPermissionsErrors( 'create', $this->user );
+ $this->assertEquals( array( array( 'nocreate-loggedin' ) ), $res );
+
+ $this->setUser( 'anon' );
+ $this->setTitle( NS_USER, $this->userName . '' );
+ $this->setUserPerm( "" );
+ $res = $this->title->getUserPermissionsErrors( 'move', $this->user );
+ $this->assertEquals( array( array( 'cant-move-user-page' ), array( 'movenologintext' ) ), $res );
+
+ $this->setTitle( NS_USER, $this->userName . '/subpage' );
+ $this->setUserPerm( "" );
+ $res = $this->title->getUserPermissionsErrors( 'move', $this->user );
+ $this->assertEquals( array( array( 'movenologintext' ) ), $res );
+
+ $this->setTitle( NS_USER, $this->userName . '' );
+ $this->setUserPerm( "move-rootuserpages" );
+ $res = $this->title->getUserPermissionsErrors( 'move', $this->user );
+ $this->assertEquals( array( array( 'movenologintext' ) ), $res );
+
+ $this->setTitle( NS_USER, $this->userName . '/subpage' );
+ $this->setUserPerm( "move-rootuserpages" );
+ $res = $this->title->getUserPermissionsErrors( 'move', $this->user );
+ $this->assertEquals( array( array( 'movenologintext' ) ), $res );
+
+ $this->setTitle( NS_USER, $this->userName . '' );
+ $this->setUserPerm( "" );
+ $res = $this->title->getUserPermissionsErrors( 'move', $this->user );
+ $this->assertEquals( array( array( 'cant-move-user-page' ), array( 'movenologintext' ) ), $res );
+
+ $this->setTitle( NS_USER, $this->userName . '/subpage' );
+ $this->setUserPerm( "" );
+ $res = $this->title->getUserPermissionsErrors( 'move', $this->user );
+ $this->assertEquals( array( array( 'movenologintext' ) ), $res );
+
+ $this->setTitle( NS_USER, $this->userName . '' );
+ $this->setUserPerm( "move-rootuserpages" );
+ $res = $this->title->getUserPermissionsErrors( 'move', $this->user );
+ $this->assertEquals( array( array( 'movenologintext' ) ), $res );
+
+ $this->setTitle( NS_USER, $this->userName . '/subpage' );
+ $this->setUserPerm( "move-rootuserpages" );
+ $res = $this->title->getUserPermissionsErrors( 'move', $this->user );
+ $this->assertEquals( array( array( 'movenologintext' ) ), $res );
+
+ $this->setUser( $this->userName );
+ $this->setTitle( NS_FILE, "img.png" );
+ $this->setUserPerm( "" );
+ $res = $this->title->getUserPermissionsErrors( 'move', $this->user );
+ $this->assertEquals( array( array( 'movenotallowedfile' ), array( 'movenotallowed' ) ), $res );
+
+ $this->setTitle( NS_FILE, "img.png" );
+ $this->setUserPerm( "movefile" );
+ $res = $this->title->getUserPermissionsErrors( 'move', $this->user );
+ $this->assertEquals( array( array( 'movenotallowed' ) ), $res );
+
+ $this->setUser( 'anon' );
+ $this->setTitle( NS_FILE, "img.png" );
+ $this->setUserPerm( "" );
+ $res = $this->title->getUserPermissionsErrors( 'move', $this->user );
+ $this->assertEquals( array( array( 'movenotallowedfile' ), array( 'movenologintext' ) ), $res );
+
+ $this->setTitle( NS_FILE, "img.png" );
+ $this->setUserPerm( "movefile" );
+ $res = $this->title->getUserPermissionsErrors( 'move', $this->user );
+ $this->assertEquals( array( array( 'movenologintext' ) ), $res );
+
+ $this->setUser( $this->userName );
+ $this->setUserPerm( "move" );
+ $this->runGroupPermissions( 'move', array( array( 'movenotallowedfile' ) ) );
+
+ $this->setUserPerm( "" );
+ $this->runGroupPermissions( 'move', array( array( 'movenotallowedfile' ), array( 'movenotallowed' ) ) );
+
+ $this->setUser( 'anon' );
+ $this->setUserPerm( "move" );
+ $this->runGroupPermissions( 'move', array( array( 'movenotallowedfile' ) ) );
+
+ $this->setUserPerm( "" );
+ $this->runGroupPermissions( 'move', array( array( 'movenotallowedfile' ), array( 'movenotallowed' ) ),
+ array( array( 'movenotallowedfile' ), array( 'movenologintext' ) ) );
+
+ $this->setTitle( NS_MAIN );
+ $this->setUser( 'anon' );
+ $this->setUserPerm( "move" );
+ $this->runGroupPermissions( 'move', array( ) );
+
+ $this->setUserPerm( "" );
+ $this->runGroupPermissions( 'move', array( array( 'movenotallowed' ) ),
+ array( array( 'movenologintext' ) ) );
+
+ $this->setUser( $this->userName );
+ $this->setUserPerm( "" );
+ $this->runGroupPermissions( 'move', array( array( 'movenotallowed' ) ) );
+
+ $this->setUserPerm( "move" );
+ $this->runGroupPermissions( 'move', array( ) );
+
+ $this->setUser( 'anon' );
+ $this->setUserPerm( 'move' );
+ $res = $this->title->getUserPermissionsErrors( 'move-target', $this->user );
+ $this->assertEquals( array( ), $res );
+
+ $this->setUserPerm( '' );
+ $res = $this->title->getUserPermissionsErrors( 'move-target', $this->user );
+ $this->assertEquals( array( array( 'movenotallowed' ) ), $res );
+
+ $this->setTitle( NS_USER );
+ $this->setUser( $this->userName );
+ $this->setUserPerm( array( "move", "move-rootuserpages" ) );
+ $res = $this->title->getUserPermissionsErrors( 'move-target', $this->user );
+ $this->assertEquals( array( ), $res );
+
+ $this->setUserPerm( "move" );
+ $res = $this->title->getUserPermissionsErrors( 'move-target', $this->user );
+ $this->assertEquals( array( array( 'cant-move-to-user-page' ) ), $res );
+
+ $this->setUser( 'anon' );
+ $this->setUserPerm( array( "move", "move-rootuserpages" ) );
+ $res = $this->title->getUserPermissionsErrors( 'move-target', $this->user );
+ $this->assertEquals( array( ), $res );
+
+ $this->setTitle( NS_USER, "User/subpage" );
+ $this->setUserPerm( array( "move", "move-rootuserpages" ) );
+ $res = $this->title->getUserPermissionsErrors( 'move-target', $this->user );
+ $this->assertEquals( array( ), $res );
+
+ $this->setUserPerm( "move" );
+ $res = $this->title->getUserPermissionsErrors( 'move-target', $this->user );
+ $this->assertEquals( array( ), $res );
+
+ $this->setUser( 'anon' );
+ $check = array( 'edit' => array( array( array( 'badaccess-groups', "*, [[$prefix:Users|Users]]", 2 ) ),
+ array( array( 'badaccess-group0' ) ),
+ array( ), true ),
+ 'protect' => array( array( array( 'badaccess-groups', "[[$prefix:Administrators|Administrators]]", 1 ), array( 'protect-cantedit' ) ),
+ array( array( 'badaccess-group0' ), array( 'protect-cantedit' ) ),
+ array( array( 'protect-cantedit' ) ), false ),
+ '' => array( array( ), array( ), array( ), true ) );
+ global $wgUser;
+ $wgUser = $this->user;
+ foreach ( array( "edit", "protect", "" ) as $action ) {
+ $this->setUserPerm( null );
+ $this->assertEquals( $check[$action][0],
+ $this->title->getUserPermissionsErrors( $action, $this->user, true ) );
+
+ global $wgGroupPermissions;
+ $old = $wgGroupPermissions;
+ $wgGroupPermissions = array();
+
+ $this->assertEquals( $check[$action][1],
+ $this->title->getUserPermissionsErrors( $action, $this->user, true ) );
+ $wgGroupPermissions = $old;
+
+ $this->setUserPerm( $action );
+ $this->assertEquals( $check[$action][2],
+ $this->title->getUserPermissionsErrors( $action, $this->user, true ) );
+
+ $this->setUserPerm( $action );
+ $this->assertEquals( $check[$action][3],
+ $this->title->userCan( $action, true ) );
+ $this->assertEquals( $check[$action][3],
+ $this->title->quickUserCan( $action, false ) );
+
+ # count( User::getGroupsWithPermissions( $action ) ) < 1
+ }
+ }
+
+ function runGroupPermissions( $action, $result, $result2 = null ) {
+ global $wgGroupPermissions;
+
+ if ( $result2 === null ) $result2 = $result;
+
+ $wgGroupPermissions['autoconfirmed']['move'] = false;
+ $wgGroupPermissions['user']['move'] = false;
+ $res = $this->title->getUserPermissionsErrors( $action, $this->user );
+ $this->assertEquals( $result, $res );
+
+ $wgGroupPermissions['autoconfirmed']['move'] = true;
+ $wgGroupPermissions['user']['move'] = false;
+ $res = $this->title->getUserPermissionsErrors( $action, $this->user );
+ $this->assertEquals( $result2, $res );
+
+ $wgGroupPermissions['autoconfirmed']['move'] = true;
+ $wgGroupPermissions['user']['move'] = true;
+ $res = $this->title->getUserPermissionsErrors( $action, $this->user );
+ $this->assertEquals( $result2, $res );
+
+ $wgGroupPermissions['autoconfirmed']['move'] = false;
+ $wgGroupPermissions['user']['move'] = true;
+ $res = $this->title->getUserPermissionsErrors( $action, $this->user );
+ $this->assertEquals( $result2, $res );
+ }
+
+ function testSpecialsAndNSPermissions() {
+ $this->setUser( $this->userName );
+ global $wgUser;
+ $wgUser = $this->user;
+
+ $this->setTitle( NS_SPECIAL );
+
+ $this->assertEquals( array( array( 'badaccess-group0' ), array( 'ns-specialprotected' ) ),
+ $this->title->getUserPermissionsErrors( 'bogus', $this->user ) );
+ $this->assertEquals( array( array( 'badaccess-group0' ) ),
+ $this->title->getUserPermissionsErrors( 'execute', $this->user ) );
+
+ $this->setTitle( NS_MAIN );
+ $this->setUserPerm( 'bogus' );
+ $this->assertEquals( array( ),
+ $this->title->getUserPermissionsErrors( 'bogus', $this->user ) );
+
+ $this->setTitle( NS_MAIN );
+ $this->setUserPerm( '' );
+ $this->assertEquals( array( array( 'badaccess-group0' ) ),
+ $this->title->getUserPermissionsErrors( 'bogus', $this->user ) );
+
+ global $wgNamespaceProtection;
+ $wgNamespaceProtection[NS_USER] = array ( 'bogus' );
+ $this->setTitle( NS_USER );
+ $this->setUserPerm( '' );
+ $this->assertEquals( array( array( 'badaccess-group0' ), array( 'namespaceprotected', 'User' ) ),
+ $this->title->getUserPermissionsErrors( 'bogus', $this->user ) );
+
+ $this->setTitle( NS_MEDIAWIKI );
+ $this->setUserPerm( 'bogus' );
+ $this->assertEquals( array( array( 'protectedinterface' ) ),
+ $this->title->getUserPermissionsErrors( 'bogus', $this->user ) );
+
+ $this->setTitle( NS_MEDIAWIKI );
+ $this->setUserPerm( 'bogus' );
+ $this->assertEquals( array( array( 'protectedinterface' ) ),
+ $this->title->getUserPermissionsErrors( 'bogus', $this->user ) );
+
+ $wgNamespaceProtection = null;
+ $this->setUserPerm( 'bogus' );
+ $this->assertEquals( array( ),
+ $this->title->getUserPermissionsErrors( 'bogus', $this->user ) );
+ $this->assertEquals( true,
+ $this->title->userCan( 'bogus' ) );
+
+ $this->setUserPerm( '' );
+ $this->assertEquals( array( array( 'badaccess-group0' ) ),
+ $this->title->getUserPermissionsErrors( 'bogus', $this->user ) );
+ $this->assertEquals( false,
+ $this->title->userCan( 'bogus' ) );
+ }
+
+ function testCssAndJavascriptPermissions() {
+ $this->setUser( $this->userName );
+ global $wgUser;
+ $wgUser = $this->user;
+
+ $this->setTitle( NS_USER, $this->altUserName . '/test.js' );
+ $this->runCSSandJSPermissions(
+ array( array( 'badaccess-group0' ), array( 'customjsprotected' ) ),
+ array( array( 'badaccess-group0' ), array( 'customjsprotected' ) ),
+ array( array( 'badaccess-group0' ) ) );
+
+ $this->setTitle( NS_USER, $this->altUserName . '/test.css' );
+ $this->runCSSandJSPermissions(
+ array( array( 'badaccess-group0' ), array( 'customcssprotected' ) ),
+ array( array( 'badaccess-group0' ) ),
+ array( array( 'badaccess-group0' ), array( 'customcssprotected' ) ) );
+
+ $this->setTitle( NS_USER, $this->altUserName . '/tempo' );
+ $this->runCSSandJSPermissions(
+ array( array( 'badaccess-group0' ) ),
+ array( array( 'badaccess-group0' ) ),
+ array( array( 'badaccess-group0' ) ) );
+ }
+
+ function runCSSandJSPermissions( $result0, $result1, $result2 ) {
+ $this->setUserPerm( '' );
+ $this->assertEquals( $result0,
+ $this->title->getUserPermissionsErrors( 'bogus',
+ $this->user ) );
+
+ $this->setUserPerm( 'editusercss' );
+ $this->assertEquals( $result1,
+ $this->title->getUserPermissionsErrors( 'bogus',
+ $this->user ) );
+
+ $this->setUserPerm( 'edituserjs' );
+ $this->assertEquals( $result2,
+ $this->title->getUserPermissionsErrors( 'bogus',
+ $this->user ) );
+
+ $this->setUserPerm( 'editusercssjs' );
+ $this->assertEquals( array( array( 'badaccess-group0' ) ),
+ $this->title->getUserPermissionsErrors( 'bogus',
+ $this->user ) );
+
+ $this->setUserPerm( array( 'edituserjs', 'editusercss' ) );
+ $this->assertEquals( array( array( 'badaccess-group0' ) ),
+ $this->title->getUserPermissionsErrors( 'bogus',
+ $this->user ) );
+ }
+
+ function testPageRestrictions() {
+ global $wgUser, $wgContLang;
+
+ $prefix = $wgContLang->getFormattedNsText( NS_PROJECT );
+
+ $wgUser = $this->user;
+ $this->setTitle( NS_MAIN );
+ $this->title->mRestrictionsLoaded = true;
+ $this->setUserPerm( "edit" );
+ $this->title->mRestrictions = array( "bogus" => array( 'bogus', "sysop", "protect", "" ) );
+
+ $this->assertEquals( array( ),
+ $this->title->getUserPermissionsErrors( 'edit',
+ $this->user ) );
+
+ $this->assertEquals( true,
+ $this->title->quickUserCan( 'edit', false ) );
+ $this->title->mRestrictions = array( "edit" => array( 'bogus', "sysop", "protect", "" ),
+ "bogus" => array( 'bogus', "sysop", "protect", "" ) );
+
+ $this->assertEquals( array( array( 'badaccess-group0' ),
+ array( 'protectedpagetext', 'bogus' ),
+ array( 'protectedpagetext', 'protect' ),
+ array( 'protectedpagetext', 'protect' ) ),
+ $this->title->getUserPermissionsErrors( 'bogus',
+ $this->user ) );
+ $this->assertEquals( array( array( 'protectedpagetext', 'bogus' ),
+ array( 'protectedpagetext', 'protect' ),
+ array( 'protectedpagetext', 'protect' ) ),
+ $this->title->getUserPermissionsErrors( 'edit',
+ $this->user ) );
+ $this->setUserPerm( "" );
+ $this->assertEquals( array( array( 'badaccess-group0' ),
+ array( 'protectedpagetext', 'bogus' ),
+ array( 'protectedpagetext', 'protect' ),
+ array( 'protectedpagetext', 'protect' ) ),
+ $this->title->getUserPermissionsErrors( 'bogus',
+ $this->user ) );
+ $this->assertEquals( array( array( 'badaccess-groups', "*, [[$prefix:Users|Users]]", 2 ),
+ array( 'protectedpagetext', 'bogus' ),
+ array( 'protectedpagetext', 'protect' ),
+ array( 'protectedpagetext', 'protect' ) ),
+ $this->title->getUserPermissionsErrors( 'edit',
+ $this->user ) );
+ $this->setUserPerm( array( "edit", "editprotected" ) );
+ $this->assertEquals( array( array( 'badaccess-group0' ),
+ array( 'protectedpagetext', 'bogus' ),
+ array( 'protectedpagetext', 'protect' ),
+ array( 'protectedpagetext', 'protect' ) ),
+ $this->title->getUserPermissionsErrors( 'bogus',
+ $this->user ) );
+ $this->assertEquals( array( ),
+ $this->title->getUserPermissionsErrors( 'edit',
+ $this->user ) );
+ $this->title->mCascadeRestriction = true;
+ $this->assertEquals( false,
+ $this->title->quickUserCan( 'bogus', false ) );
+ $this->assertEquals( false,
+ $this->title->quickUserCan( 'edit', false ) );
+ $this->assertEquals( array( array( 'badaccess-group0' ),
+ array( 'protectedpagetext', 'bogus' ),
+ array( 'protectedpagetext', 'protect' ),
+ array( 'protectedpagetext', 'protect' ) ),
+ $this->title->getUserPermissionsErrors( 'bogus',
+ $this->user ) );
+ $this->assertEquals( array( array( 'protectedpagetext', 'bogus' ),
+ array( 'protectedpagetext', 'protect' ),
+ array( 'protectedpagetext', 'protect' ) ),
+ $this->title->getUserPermissionsErrors( 'edit',
+ $this->user ) );
+ }
+
+ function testCascadingSourcesRestrictions() {
+ global $wgUser;
+ $wgUser = $this->user;
+ $this->setTitle( NS_MAIN, "test page" );
+ $this->setUserPerm( array( "edit", "bogus" ) );
+
+ $this->title->mCascadeSources = array( Title::makeTitle( NS_MAIN, "Bogus" ), Title::makeTitle( NS_MAIN, "UnBogus" ) );
+ $this->title->mCascadingRestrictions = array( "bogus" => array( 'bogus', "sysop", "protect", "" ) );
+
+ $this->assertEquals( false,
+ $this->title->userCan( 'bogus' ) );
+ $this->assertEquals( array( array( "cascadeprotected", 2, "* [[:Bogus]]\n* [[:UnBogus]]\n" ),
+ array( "cascadeprotected", 2, "* [[:Bogus]]\n* [[:UnBogus]]\n" ) ),
+ $this->title->getUserPermissionsErrors( 'bogus', $this->user ) );
+
+ $this->assertEquals( true,
+ $this->title->userCan( 'edit' ) );
+ $this->assertEquals( array( ),
+ $this->title->getUserPermissionsErrors( 'edit', $this->user ) );
+
+ }
+
+ function testActionPermissions() {
+ global $wgUser;
+ $wgUser = $this->user;
+
+ $this->setUserPerm( array( "createpage" ) );
+ $this->setTitle( NS_MAIN, "test page" );
+ $this->title->mTitleProtection['pt_create_perm'] = '';
+ $this->title->mTitleProtection['pt_user'] = $this->user->getID();
+ $this->title->mTitleProtection['pt_expiry'] = Block::infinity();
+ $this->title->mTitleProtection['pt_reason'] = 'test';
+ $this->title->mCascadeRestriction = false;
+
+ $this->assertEquals( array( array( 'titleprotected', 'Useruser', 'test' ) ),
+ $this->title->getUserPermissionsErrors( 'create', $this->user ) );
+ $this->assertEquals( false,
+ $this->title->userCan( 'create' ) );
+
+ $this->title->mTitleProtection['pt_create_perm'] = 'sysop';
+ $this->setUserPerm( array( 'createpage', 'protect' ) );
+ $this->assertEquals( array( ),
+ $this->title->getUserPermissionsErrors( 'create', $this->user ) );
+ $this->assertEquals( true,
+ $this->title->userCan( 'create' ) );
+
+
+ $this->setUserPerm( array( 'createpage' ) );
+ $this->assertEquals( array( array( 'titleprotected', 'Useruser', 'test' ) ),
+ $this->title->getUserPermissionsErrors( 'create', $this->user ) );
+ $this->assertEquals( false,
+ $this->title->userCan( 'create' ) );
+
+ $this->setTitle( NS_MEDIA, "test page" );
+ $this->setUserPerm( array( "move" ) );
+ $this->assertEquals( false,
+ $this->title->userCan( 'move' ) );
+ $this->assertEquals( array( array( 'immobile-source-namespace', 'Media' ) ),
+ $this->title->getUserPermissionsErrors( 'move', $this->user ) );
+
+ $this->setTitle( NS_MAIN, "test page" );
+ $this->assertEquals( array( ),
+ $this->title->getUserPermissionsErrors( 'move', $this->user ) );
+ $this->assertEquals( true,
+ $this->title->userCan( 'move' ) );
+
+ $this->title->mInterwiki = "no";
+ $this->assertEquals( array( array( 'immobile-page' ) ),
+ $this->title->getUserPermissionsErrors( 'move', $this->user ) );
+ $this->assertEquals( false,
+ $this->title->userCan( 'move' ) );
+
+ $this->setTitle( NS_MEDIA, "test page" );
+ $this->assertEquals( false,
+ $this->title->userCan( 'move-target' ) );
+ $this->assertEquals( array( array( 'immobile-target-namespace', 'Media' ) ),
+ $this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
+
+ $this->setTitle( NS_MAIN, "test page" );
+ $this->assertEquals( array( ),
+ $this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
+ $this->assertEquals( true,
+ $this->title->userCan( 'move-target' ) );
+
+ $this->title->mInterwiki = "no";
+ $this->assertEquals( array( array( 'immobile-target-page' ) ),
+ $this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
+ $this->assertEquals( false,
+ $this->title->userCan( 'move-target' ) );
+
+ }
+
+ function testUserBlock() {
+ global $wgUser, $wgEmailConfirmToEdit, $wgEmailAuthentication;
+ $wgEmailConfirmToEdit = true;
+ $wgEmailAuthentication = true;
+ $wgUser = $this->user;
+
+ $this->setUserPerm( array( "createpage", "move" ) );
+ $this->setTitle( NS_MAIN, "test page" );
+
+ # $short
+ $this->assertEquals( array( array( 'confirmedittext' ) ),
+ $this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
+ $wgEmailConfirmToEdit = false;
+ $this->assertEquals( true, $this->title->userCan( 'move-target' ) );
+
+ # $wgEmailConfirmToEdit && !$user->isEmailConfirmed() && $action != 'createaccount'
+ $this->assertEquals( array( ),
+ $this->title->getUserPermissionsErrors( 'move-target',
+ $this->user ) );
+
+ global $wgLang;
+ $prev = time();
+ $now = time() + 120;
+ $this->user->mBlockedby = $this->user->getId();
+ $this->user->mBlock = new Block( '127.0.8.1', $this->user->getId(), $this->user->getId(),
+ 'no reason given', $prev + 3600, 1, 0 );
+ $this->user->mBlock->mTimestamp = 0;
+ $this->assertEquals( array( array( 'autoblockedtext',
+ '[[User:Useruser|Useruser]]', 'no reason given', '127.0.0.1',
+ 'Useruser', null, 'infinite', '127.0.8.1',
+ $wgLang->timeanddate( wfTimestamp( TS_MW, $prev ), true ) ) ),
+ $this->title->getUserPermissionsErrors( 'move-target',
+ $this->user ) );
+
+ $this->assertEquals( false, $this->title->userCan( 'move-target' ) );
+ // quickUserCan should ignore user blocks
+ $this->assertEquals( true, $this->title->quickUserCan( 'move-target' ) );
+
+ global $wgLocalTZoffset;
+ $wgLocalTZoffset = -60;
+ $this->user->mBlockedby = $this->user->getName();
+ $this->user->mBlock = new Block( '127.0.8.1', 2, 1, 'no reason given', $now, 0, 10 );
+ $this->assertEquals( array( array( 'blockedtext',
+ '[[User:Useruser|Useruser]]', 'no reason given', '127.0.0.1',
+ 'Useruser', null, '23:00, 31 December 1969', '127.0.8.1',
+ $wgLang->timeanddate( wfTimestamp( TS_MW, $now ), true ) ) ),
+ $this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
+
+ # $action != 'read' && $action != 'createaccount' && $user->isBlockedFrom( $this )
+ # $user->blockedFor() == ''
+ # $user->mBlock->mExpiry == 'infinity'
+ }
+}
diff --git a/tests/phpunit/includes/TitleTest.php b/tests/phpunit/includes/TitleTest.php
new file mode 100644
index 00000000..51b36160
--- /dev/null
+++ b/tests/phpunit/includes/TitleTest.php
@@ -0,0 +1,79 @@
+<?php
+
+class TitleTest extends MediaWikiTestCase {
+
+ function testLegalChars() {
+ $titlechars = Title::legalChars();
+
+ foreach ( range( 1, 255 ) as $num ) {
+ $chr = chr( $num );
+ if ( strpos( "#[]{}<>|", $chr ) !== false || preg_match( "/[\\x00-\\x1f\\x7f]/", $chr ) ) {
+ $this->assertFalse( (bool)preg_match( "/[$titlechars]/", $chr ), "chr($num) = $chr is not a valid titlechar" );
+ } else {
+ $this->assertTrue( (bool)preg_match( "/[$titlechars]/", $chr ), "chr($num) = $chr is a valid titlechar" );
+ }
+ }
+ }
+
+ /**
+ * @dataProvider dataBug31100
+ */
+ function testBug31100FixSpecialName( $text, $expectedParam ) {
+ $title = Title::newFromText( $text );
+ $fixed = $title->fixSpecialName();
+ $stuff = explode( '/', $fixed->getDbKey(), 2 );
+ if ( count( $stuff ) == 2 ) {
+ $par = $stuff[1];
+ } else {
+ $par = null;
+ }
+ $this->assertEquals( $expectedParam, $par, "Bug 31100 regression check: Title->fixSpecialName() should preserve parameter" );
+ }
+
+ function dataBug31100() {
+ return array(
+ array( 'Special:Version', null ),
+ array( 'Special:Version/', '' ),
+ array( 'Special:Version/param', 'param' ),
+ );
+ }
+
+ /**
+ * Auth-less test of Title::isValidMoveOperation
+ *
+ * @param string $source
+ * @param string $target
+ * @param array|string|true $requiredErrors
+ * @dataProvider dataTestIsValidMoveOperation
+ */
+ function testIsValidMoveOperation( $source, $target, $expected ) {
+ $title = Title::newFromText( $source );
+ $nt = Title::newFromText( $target );
+ $errors = $title->isValidMoveOperation( $nt, false );
+ if ( $expected === true ) {
+ $this->assertTrue( $errors );
+ } else {
+ $errors = $this->flattenErrorsArray( $errors );
+ foreach ( (array)$expected as $error ) {
+ $this->assertContains( $error, $errors );
+ }
+ }
+ }
+
+ function flattenErrorsArray( $errors ) {
+ $result = array();
+ foreach ( $errors as $error ) {
+ $result[] = $error[0];
+ }
+ return $result;
+ }
+
+ function dataTestIsValidMoveOperation() {
+ return array(
+ array( 'Test', 'Test', 'selfmove' ),
+ array( 'File:Test.jpg', 'Page', 'imagenocrossnamespace' )
+ );
+ }
+
+
+}
diff --git a/tests/phpunit/includes/UserIsValidEmailAddrTest.php b/tests/phpunit/includes/UserIsValidEmailAddrTest.php
new file mode 100644
index 00000000..99bf718e
--- /dev/null
+++ b/tests/phpunit/includes/UserIsValidEmailAddrTest.php
@@ -0,0 +1,79 @@
+<?php
+
+class UserIsValidEmailAddrTest extends MediaWikiTestCase {
+
+ private function checkEmail( $addr, $expected = true, $msg = '') {
+ if( $msg == '' ) { $msg = "Testing $addr"; }
+ $this->assertEquals(
+ $expected,
+ User::isValidEmailAddr( $addr ),
+ $msg
+ );
+ }
+ private function valid( $addr, $msg = '' ) {
+ $this->checkEmail( $addr, true, $msg );
+ }
+ private function invalid( $addr, $msg = '' ) {
+ $this->checkEmail( $addr, false, $msg );
+ }
+
+ function testEmailWellKnownUserAtHostDotTldAreValid() {
+ $this->valid( 'user@example.com' );
+ $this->valid( 'user@example.museum' );
+ }
+ function testEmailWithUpperCaseCharactersAreValid() {
+ $this->valid( 'USER@example.com' );
+ $this->valid( 'user@EXAMPLE.COM' );
+ $this->valid( 'user@Example.com' );
+ $this->valid( 'USER@eXAMPLE.com' );
+ }
+ function testEmailWithAPlusInUserName() {
+ $this->valid( 'user+sub@example.com' );
+ $this->valid( 'user+@example.com' );
+ }
+ function testEmailDoesNotNeedATopLevelDomain() {
+ $this->valid( "user@localhost" );
+ $this->valid( "FooBar@localdomain" );
+ $this->valid( "nobody@mycompany" );
+ }
+ function testEmailWithWhiteSpacesBeforeOrAfterAreInvalids() {
+ $this->invalid( " user@host.com" );
+ $this->invalid( "user@host.com " );
+ $this->invalid( "\tuser@host.com" );
+ $this->invalid( "user@host.com\t" );
+ }
+ function testEmailWithWhiteSpacesAreInvalids() {
+ $this->invalid( "User user@host" );
+ $this->invalid( "first last@mycompany" );
+ $this->invalid( "firstlast@my company" );
+ }
+ // bug 26948 : comma were matched by an incorrect regexp range
+ function testEmailWithCommasAreInvalids() {
+ $this->invalid( "user,foo@example.org" );
+ $this->invalid( "userfoo@ex,ample.org" );
+ }
+ function testEmailWithHyphens() {
+ $this->valid( "user-foo@example.org" );
+ $this->valid( "userfoo@ex-ample.org" );
+ }
+ function testEmailDomainCanNotBeginWithDot() {
+ $this->invalid( "user@." );
+ $this->invalid( "user@.localdomain" );
+ $this->invalid( "user@localdomain." );
+ $this->valid( "user.@localdomain" );
+ $this->valid( ".@localdomain" );
+ $this->invalid( ".@a............" );
+ }
+ function testEmailWithFunnyCharacters() {
+ $this->valid( "\$user!ex{this}@123.com" );
+ }
+ function testEmailTopLevelDomainCanBeNumerical() {
+ $this->valid( "user@example.1234" );
+ }
+ function testEmailWithoutAtSignIsInvalid() {
+ $this->invalid( 'useràexample.com' );
+ }
+ function testEmailWithOneCharacterDomainIsValid() {
+ $this->valid( 'user@a' );
+ }
+}
diff --git a/tests/phpunit/includes/UserTest.php b/tests/phpunit/includes/UserTest.php
new file mode 100644
index 00000000..df91aca8
--- /dev/null
+++ b/tests/phpunit/includes/UserTest.php
@@ -0,0 +1,58 @@
+<?php
+
+class UserTest extends MediaWikiTestCase {
+ protected $savedGroupPermissions, $savedRevokedPermissions;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->savedGroupPermissions = $GLOBALS['wgGroupPermissions'];
+ $this->savedRevokedPermissions = $GLOBALS['wgRevokePermissions'];
+
+ $this->setUpPermissionGlobals();
+ }
+ private function setUpPermissionGlobals() {
+ global $wgGroupPermissions, $wgRevokePermissions;
+
+ $wgGroupPermissions['unittesters'] = array(
+ 'runtest' => true,
+ 'writetest' => false,
+ 'nukeworld' => false,
+ );
+ $wgGroupPermissions['testwriters'] = array(
+ 'writetest' => true,
+ 'modifytest' => true,
+ );
+
+ $wgRevokePermissions['formertesters'] = array(
+ 'runtest' => true,
+ );
+ }
+ public function tearDown() {
+ parent::tearDown();
+
+ $GLOBALS['wgGroupPermissions'] = $this->savedGroupPermissions;
+ $GLOBALS['wgRevokePermissions'] = $this->savedRevokedPermissions;
+ }
+
+ public function testGroupPermissions() {
+ $rights = User::getGroupPermissions( array( 'unittesters' ) );
+ $this->assertContains( 'runtest', $rights );
+ $this->assertNotContains( 'writetest', $rights );
+ $this->assertNotContains( 'modifytest', $rights );
+ $this->assertNotContains( 'nukeworld', $rights );
+
+ $rights = User::getGroupPermissions( array( 'unittesters', 'testwriters' ) );
+ $this->assertContains( 'runtest', $rights );
+ $this->assertContains( 'writetest', $rights );
+ $this->assertContains( 'modifytest', $rights );
+ $this->assertNotContains( 'nukeworld', $rights );
+ }
+ public function testRevokePermissions() {
+ $rights = User::getGroupPermissions( array( 'unittesters', 'formertesters' ) );
+ $this->assertNotContains( 'runtest', $rights );
+ $this->assertNotContains( 'writetest', $rights );
+ $this->assertNotContains( 'modifytest', $rights );
+ $this->assertNotContains( 'nukeworld', $rights );
+ }
+} \ No newline at end of file
diff --git a/tests/phpunit/includes/WebRequestTest.php b/tests/phpunit/includes/WebRequestTest.php
new file mode 100644
index 00000000..1cfbd3fc
--- /dev/null
+++ b/tests/phpunit/includes/WebRequestTest.php
@@ -0,0 +1,88 @@
+<?php
+
+class WebRequestTest extends MediaWikiTestCase {
+ /**
+ * @dataProvider provideDetectServer
+ */
+ function testDetectServer( $expected, $input, $description ) {
+ $oldServer = $_SERVER;
+ $_SERVER = $input;
+ $result = WebRequest::detectServer();
+ $_SERVER = $oldServer;
+ $this->assertEquals( $expected, $result, $description );
+ }
+
+ function provideDetectServer() {
+ return array(
+ array(
+ 'http://x',
+ array(
+ 'HTTP_HOST' => 'x'
+ ),
+ 'Host header'
+ ),
+ array(
+ 'https://x',
+ array(
+ 'HTTP_HOST' => 'x',
+ 'HTTPS' => 'on',
+ ),
+ 'Host header with secure'
+ ),
+ array(
+ 'http://x',
+ array(
+ 'HTTP_HOST' => 'x',
+ 'SERVER_PORT' => 80,
+ ),
+ 'Default SERVER_PORT',
+ ),
+ array(
+ 'http://x',
+ array(
+ 'HTTP_HOST' => 'x',
+ 'HTTPS' => 'off',
+ ),
+ 'Secure off'
+ ),
+ array(
+ 'http://y',
+ array(
+ 'SERVER_NAME' => 'y',
+ ),
+ 'Server name'
+ ),
+ array(
+ 'http://x',
+ array(
+ 'HTTP_HOST' => 'x',
+ 'SERVER_NAME' => 'y',
+ ),
+ 'Host server name precedence'
+ ),
+ array(
+ 'http://[::1]:81',
+ array(
+ 'HTTP_HOST' => '[::1]',
+ 'SERVER_NAME' => '::1',
+ 'SERVER_PORT' => '81',
+ ),
+ 'Apache bug 26005'
+ ),
+ array(
+ 'http://localhost',
+ array(
+ 'SERVER_NAME' => '[2001'
+ ),
+ 'Kind of like lighttpd per commit message in MW r83847',
+ ),
+ array(
+ 'http://[2a01:e35:2eb4:1::2]:777',
+ array(
+ 'SERVER_NAME' => '[2a01:e35:2eb4:1::2]:777'
+ ),
+ 'Possible lighttpd environment per bug 14977 comment 13',
+ ),
+ );
+ }
+}
diff --git a/tests/phpunit/includes/XmlJsTest.php b/tests/phpunit/includes/XmlJsTest.php
new file mode 100644
index 00000000..c5b411fb
--- /dev/null
+++ b/tests/phpunit/includes/XmlJsTest.php
@@ -0,0 +1,9 @@
+<?php
+class XmlJs extends MediaWikiTestCase {
+ public function testConstruction() {
+ $obj = new XmlJsCode( null );
+ $this->assertNull( $obj->value );
+ $obj = new XmlJsCode( '' );
+ $this->assertSame( $obj->value, '' );
+ }
+}
diff --git a/tests/phpunit/includes/XmlSelectTest.php b/tests/phpunit/includes/XmlSelectTest.php
new file mode 100644
index 00000000..bf761e3d
--- /dev/null
+++ b/tests/phpunit/includes/XmlSelectTest.php
@@ -0,0 +1,139 @@
+<?php
+
+// TODO
+class XmlSelectTest extends MediaWikiTestCase {
+ protected $select;
+
+ protected function setUp() {
+ $this->select = new XmlSelect();
+ }
+ protected function tearDown() {
+ $this->select = null;
+ }
+
+ ### START OF TESTS ###
+
+ public function testConstructWithoutParameters() {
+ $this->assertEquals( '<select></select>', $this->select->getHTML() );
+ }
+
+ /**
+ * Parameters are $name (false), $id (false), $default (false)
+ * @dataProvider provideConstructionParameters
+ */
+ public function testConstructParameters( $name, $id, $default, $expected ) {
+ $this->select = new XmlSelect( $name, $id, $default );
+ $this->assertEquals( $expected, $this->select->getHTML() );
+ }
+
+ /**
+ * Provide parameters for testConstructParameters() which use three
+ * parameters:
+ * - $name (default: false)
+ * - $id (default: false)
+ * - $default (default: false)
+ * Provides a fourth parameters representing the expected HTML output
+ *
+ */
+ public function provideConstructionParameters() {
+ return array(
+ /**
+ * Values are set following a 3-bit Gray code where two successive
+ * values differ by only one value.
+ * See http://en.wikipedia.org/wiki/Gray_code
+ */
+ # $name $id $default
+ array( false , false, false, '<select></select>' ),
+ array( false , false, 'foo', '<select></select>' ),
+ array( false , 'id' , 'foo', '<select id="id"></select>' ),
+ array( false , 'id' , false, '<select id="id"></select>' ),
+ array( 'name', 'id' , false, '<select name="name" id="id"></select>' ),
+ array( 'name', 'id' , 'foo', '<select name="name" id="id"></select>' ),
+ array( 'name', false, 'foo', '<select name="name"></select>' ),
+ array( 'name', false, false, '<select name="name"></select>' ),
+ );
+ }
+
+ # Begin XmlSelect::addOption() similar to Xml::option
+ public function testAddOption() {
+ $this->select->addOption( 'foo' );
+ $this->assertEquals( '<select><option value="foo">foo</option></select>', $this->select->getHTML() );
+ }
+ public function testAddOptionWithDefault() {
+ $this->select->addOption( 'foo', true );
+ $this->assertEquals( '<select><option value="1">foo</option></select>', $this->select->getHTML() );
+ }
+ public function testAddOptionWithFalse() {
+ $this->select->addOption( 'foo', false );
+ $this->assertEquals( '<select><option value="foo">foo</option></select>', $this->select->getHTML() );
+ }
+ public function testAddOptionWithValueZero() {
+ $this->select->addOption( 'foo', 0 );
+ $this->assertEquals( '<select><option value="0">foo</option></select>', $this->select->getHTML() );
+ }
+ # End XmlSelect::addOption() similar to Xml::option
+
+ public function testSetDefault() {
+ $this->select->setDefault( 'bar1' );
+ $this->select->addOption( 'foo1' );
+ $this->select->addOption( 'bar1' );
+ $this->select->addOption( 'foo2' );
+ $this->assertEquals(
+'<select><option value="foo1">foo1</option>' . "\n" .
+'<option value="bar1" selected="selected">bar1</option>' . "\n" .
+'<option value="foo2">foo2</option></select>', $this->select->getHTML() );
+ }
+
+ /**
+ * Adding default later on should set the correct selection or
+ * raise an exception.
+ * To handle this, we need to render the options in getHtml()
+ */
+ public function testSetDefaultAfterAddingOptions() {
+ $this->select->addOption( 'foo1' );
+ $this->select->addOption( 'bar1' );
+ $this->select->addOption( 'foo2' );
+ $this->select->setDefault( 'bar1' ); # setting default after adding options
+ $this->assertEquals(
+'<select><option value="foo1">foo1</option>' . "\n" .
+'<option value="bar1" selected="selected">bar1</option>' . "\n" .
+'<option value="foo2">foo2</option></select>', $this->select->getHTML() );
+ }
+
+ public function testGetAttributes() {
+ # create some attributes
+ $this->select->setAttribute( 'dummy', 0x777 );
+ $this->select->setAttribute( 'string', 'euro €' );
+ $this->select->setAttribute( 1911, 'razor' );
+
+ # verify we can retrieve them
+ $this->assertEquals(
+ $this->select->getAttribute( 'dummy' ),
+ 0x777
+ );
+ $this->assertEquals(
+ $this->select->getAttribute( 'string' ),
+ 'euro €'
+ );
+ $this->assertEquals(
+ $this->select->getAttribute( 1911 ),
+ 'razor'
+ );
+
+ # inexistant keys should give us 'null'
+ $this->assertEquals(
+ $this->select->getAttribute( 'I DO NOT EXIT' ),
+ null
+ );
+
+ # verify string / integer
+ $this->assertEquals(
+ $this->select->getAttribute( '1911' ),
+ 'razor'
+ );
+ $this->assertEquals(
+ $this->select->getAttribute( 'dummy' ),
+ 0x777
+ );
+ }
+}
diff --git a/tests/phpunit/includes/XmlTest.php b/tests/phpunit/includes/XmlTest.php
new file mode 100644
index 00000000..fbb498d8
--- /dev/null
+++ b/tests/phpunit/includes/XmlTest.php
@@ -0,0 +1,304 @@
+<?php
+
+class XmlTest extends MediaWikiTestCase {
+ private static $oldLang;
+
+ public function setUp() {
+ global $wgLang, $wgLanguageCode;
+
+ self::$oldLang = $wgLang;
+ $wgLanguageCode = 'en';
+ $wgLang = Language::factory( $wgLanguageCode );
+ }
+
+ public function tearDown() {
+ global $wgLang, $wgLanguageCode;
+ $wgLang = self::$oldLang;
+ $wgLanguageCode = $wgLang->getCode();
+ }
+
+ public function testExpandAttributes() {
+ $this->assertNull( Xml::expandAttributes(null),
+ 'Converting a null list of attributes'
+ );
+ $this->assertEquals( '', Xml::expandAttributes( array() ),
+ 'Converting an empty list of attributes'
+ );
+ }
+
+ public function testExpandAttributesException() {
+ $this->setExpectedException('MWException');
+ Xml::expandAttributes('string');
+ }
+
+ function testElementOpen() {
+ $this->assertEquals(
+ '<element>',
+ Xml::element( 'element', null, null ),
+ 'Opening element with no attributes'
+ );
+ }
+
+ function testElementEmpty() {
+ $this->assertEquals(
+ '<element />',
+ Xml::element( 'element', null, '' ),
+ 'Terminated empty element'
+ );
+ }
+
+ function testElementInputCanHaveAValueOfZero() {
+ $this->assertEquals(
+ '<input name="name" value="0" />',
+ Xml::input( 'name', false, 0 ),
+ 'Input with a value of 0 (bug 23797)'
+ );
+ }
+ function testElementEscaping() {
+ $this->assertEquals(
+ '<element>hello &lt;there&gt; you &amp; you</element>',
+ Xml::element( 'element', null, 'hello <there> you & you' ),
+ 'Element with no attributes and content that needs escaping'
+ );
+ }
+
+ public function testEscapeTagsOnly() {
+ $this->assertEquals( '&quot;&gt;&lt;', Xml::escapeTagsOnly( '"><' ),
+ 'replace " > and < with their HTML entitites'
+ );
+ }
+
+ function testElementAttributes() {
+ $this->assertEquals(
+ '<element key="value" <>="&lt;&gt;">',
+ Xml::element( 'element', array( 'key' => 'value', '<>' => '<>' ), null ),
+ 'Element attributes, keys are not escaped'
+ );
+ }
+
+ function testOpenElement() {
+ $this->assertEquals(
+ '<element k="v">',
+ Xml::openElement( 'element', array( 'k' => 'v' ) ),
+ 'openElement() shortcut'
+ );
+ }
+
+ function testCloseElement() {
+ $this->assertEquals( '</element>', Xml::closeElement( 'element' ), 'closeElement() shortcut' );
+ }
+
+ public function testDateMenu( ) {
+ $curYear = intval(gmdate('Y'));
+ $prevYear = $curYear - 1;
+
+ $curMonth = intval(gmdate('n'));
+ $prevMonth = $curMonth - 1;
+ if( $prevMonth == 0 ) { $prevMonth = 12; }
+ $nextMonth = $curMonth + 1;
+ if( $nextMonth == 13 ) { $nextMonth = 1; }
+
+
+ $this->assertEquals(
+ '<label for="year">From year (and earlier):</label> <input name="year" size="4" value="2011" id="year" maxlength="4" /> <label for="month">From month (and earlier):</label> <select id="month" name="month" class="mw-month-selector"><option value="-1">all</option>' . "\n" .
+'<option value="1">January</option>' . "\n" .
+'<option value="2" selected="selected">February</option>' . "\n" .
+'<option value="3">March</option>' . "\n" .
+'<option value="4">April</option>' . "\n" .
+'<option value="5">May</option>' . "\n" .
+'<option value="6">June</option>' . "\n" .
+'<option value="7">July</option>' . "\n" .
+'<option value="8">August</option>' . "\n" .
+'<option value="9">September</option>' . "\n" .
+'<option value="10">October</option>' . "\n" .
+'<option value="11">November</option>' . "\n" .
+'<option value="12">December</option></select>',
+ Xml::dateMenu( 2011, 02 ),
+ "Date menu for february 2011"
+ );
+ $this->assertEquals(
+ '<label for="year">From year (and earlier):</label> <input name="year" size="4" value="2011" id="year" maxlength="4" /> <label for="month">From month (and earlier):</label> <select id="month" name="month" class="mw-month-selector"><option value="-1">all</option>' . "\n" .
+'<option value="1">January</option>' . "\n" .
+'<option value="2">February</option>' . "\n" .
+'<option value="3">March</option>' . "\n" .
+'<option value="4">April</option>' . "\n" .
+'<option value="5">May</option>' . "\n" .
+'<option value="6">June</option>' . "\n" .
+'<option value="7">July</option>' . "\n" .
+'<option value="8">August</option>' . "\n" .
+'<option value="9">September</option>' . "\n" .
+'<option value="10">October</option>' . "\n" .
+'<option value="11">November</option>' . "\n" .
+'<option value="12">December</option></select>',
+ Xml::dateMenu( 2011, -1),
+ "Date menu with negative month for 'All'"
+ );
+ $this->assertEquals(
+ Xml::dateMenu( $curYear, $curMonth ),
+ Xml::dateMenu( '' , $curMonth ),
+ "Date menu year is the current one when not specified"
+ );
+ $this->assertEquals(
+ Xml::dateMenu( $prevYear, $nextMonth ),
+ Xml::dateMenu( '', $nextMonth ),
+ "Date menu next month is 11 months ago"
+ );
+
+ # @todo FIXME: Please note there is no year there!
+ $this->assertEquals(
+ '<label for="year">From year (and earlier):</label> <input name="year" size="4" value="" id="year" maxlength="4" /> <label for="month">From month (and earlier):</label> <select id="month" name="month" class="mw-month-selector"><option value="-1">all</option>' . "\n" .
+'<option value="1">January</option>' . "\n" .
+'<option value="2">February</option>' . "\n" .
+'<option value="3">March</option>' . "\n" .
+'<option value="4">April</option>' . "\n" .
+'<option value="5">May</option>' . "\n" .
+'<option value="6">June</option>' . "\n" .
+'<option value="7">July</option>' . "\n" .
+'<option value="8">August</option>' . "\n" .
+'<option value="9">September</option>' . "\n" .
+'<option value="10">October</option>' . "\n" .
+'<option value="11">November</option>' . "\n" .
+'<option value="12">December</option></select>',
+ Xml::dateMenu( '', ''),
+ "Date menu with neither year or month"
+ );
+ }
+
+ #
+ # textarea
+ #
+ function testTextareaNoContent() {
+ $this->assertEquals(
+ '<textarea name="name" id="name" cols="40" rows="5"></textarea>',
+ Xml::textarea( 'name', '' ),
+ 'textarea() with not content'
+ );
+ }
+
+ function testTextareaAttribs() {
+ $this->assertEquals(
+ '<textarea name="name" id="name" cols="20" rows="10">&lt;txt&gt;</textarea>',
+ Xml::textarea( 'name', '<txt>', 20, 10 ),
+ 'textarea() with custom attribs'
+ );
+ }
+
+ #
+ # input and label
+ #
+ function testLabelCreation() {
+ $this->assertEquals(
+ '<label for="id">name</label>',
+ Xml::label( 'name', 'id' ),
+ 'label() with no attribs'
+ );
+ }
+ function testLabelAttributeCanOnlyBeClassOrTitle() {
+ $this->assertEquals(
+ '<label for="id">name</label>',
+ Xml::label( 'name', 'id', array( 'generated' => true ) ),
+ 'label() can not be given a generated attribute'
+ );
+ $this->assertEquals(
+ '<label for="id" class="nice">name</label>',
+ Xml::label( 'name', 'id', array( 'class' => 'nice' ) ),
+ 'label() can get a class attribute'
+ );
+ $this->assertEquals(
+ '<label for="id" title="nice tooltip">name</label>',
+ Xml::label( 'name', 'id', array( 'title' => 'nice tooltip' ) ),
+ 'label() can get a title attribute'
+ );
+ $this->assertEquals(
+ '<label for="id" class="nice" title="nice tooltip">name</label>',
+ Xml::label( 'name', 'id', array(
+ 'generated' => true,
+ 'class' => 'nice',
+ 'title' => 'nice tooltip',
+ 'anotherattr' => 'value',
+ )
+ ),
+ 'label() skip all attributes but "class" and "title"'
+ );
+ }
+
+ #
+ # JS
+ #
+ function testEscapeJsStringSpecialChars() {
+ $this->assertEquals(
+ '\\\\\r\n',
+ Xml::escapeJsString( "\\\r\n" ),
+ 'escapeJsString() with special characters'
+ );
+ }
+
+ function testEncodeJsVarBoolean() {
+ $this->assertEquals(
+ 'true',
+ Xml::encodeJsVar( true ),
+ 'encodeJsVar() with boolean'
+ );
+ }
+
+ function testEncodeJsVarNull() {
+ $this->assertEquals(
+ 'null',
+ Xml::encodeJsVar( null ),
+ 'encodeJsVar() with null'
+ );
+ }
+
+ function testEncodeJsVarArray() {
+ $this->assertEquals(
+ '["a", 1]',
+ Xml::encodeJsVar( array( 'a', 1 ) ),
+ 'encodeJsVar() with array'
+ );
+ $this->assertEquals(
+ '{"a": "a", "b": 1}',
+ Xml::encodeJsVar( array( 'a' => 'a', 'b' => 1 ) ),
+ 'encodeJsVar() with associative array'
+ );
+ }
+
+ function testEncodeJsVarObject() {
+ $this->assertEquals(
+ '{"a": "a", "b": 1}',
+ Xml::encodeJsVar( (object)array( 'a' => 'a', 'b' => 1 ) ),
+ 'encodeJsVar() with object'
+ );
+ }
+
+ function testEncodeJsVarInt() {
+ $this->assertEquals(
+ '123456',
+ Xml::encodeJsVar( 123456 ),
+ 'encodeJsVar() with int'
+ );
+ }
+
+ function testEncodeJsVarFloat() {
+ $this->assertEquals(
+ '1.23456',
+ Xml::encodeJsVar( 1.23456 ),
+ 'encodeJsVar() with float'
+ );
+ }
+
+ function testEncodeJsVarIntString() {
+ $this->assertEquals(
+ '"123456"',
+ Xml::encodeJsVar( '123456' ),
+ 'encodeJsVar() with int-like string'
+ );
+ }
+
+ function testEncodeJsVarFloatString() {
+ $this->assertEquals(
+ '"1.23456"',
+ Xml::encodeJsVar( '1.23456' ),
+ 'encodeJsVar() with float-like string'
+ );
+ }
+}
diff --git a/tests/phpunit/includes/ZipDirectoryReaderTest.php b/tests/phpunit/includes/ZipDirectoryReaderTest.php
new file mode 100644
index 00000000..f7ca59e2
--- /dev/null
+++ b/tests/phpunit/includes/ZipDirectoryReaderTest.php
@@ -0,0 +1,79 @@
+<?php
+
+class ZipDirectoryReaderTest extends MediaWikiTestCase {
+ var $zipDir, $entries;
+
+ function setUp() {
+ $this->zipDir = dirname( __FILE__ ) . '/../data/zip';
+ }
+
+ function zipCallback( $entry ) {
+ $this->entries[] = $entry;
+ }
+
+ function readZipAssertError( $file, $error, $assertMessage ) {
+ $this->entries = array();
+ $status = ZipDirectoryReader::read( "{$this->zipDir}/$file", array( $this, 'zipCallback' ) );
+ $this->assertTrue( $status->hasMessage( $error ), $assertMessage );
+ }
+
+ function readZipAssertSuccess( $file, $assertMessage ) {
+ $this->entries = array();
+ $status = ZipDirectoryReader::read( "{$this->zipDir}/$file", array( $this, 'zipCallback' ) );
+ $this->assertTrue( $status->isOK(), $assertMessage );
+ }
+
+ function testEmpty() {
+ $this->readZipAssertSuccess( 'empty.zip', 'Empty zip' );
+ }
+
+ function testMultiDisk0() {
+ $this->readZipAssertError( 'split.zip', 'zip-unsupported',
+ 'Split zip error' );
+ }
+
+ function testNoSignature() {
+ $this->readZipAssertError( 'nosig.zip', 'zip-wrong-format',
+ 'No signature should give "wrong format" error' );
+ }
+
+ function testSimple() {
+ $this->readZipAssertSuccess( 'class.zip', 'Simple ZIP' );
+ $this->assertEquals( $this->entries, array( array(
+ 'name' => 'Class.class',
+ 'mtime' => '20010115000000',
+ 'size' => 1,
+ ) ) );
+ }
+
+ function testBadCentralEntrySignature() {
+ $this->readZipAssertError( 'wrong-central-entry-sig.zip', 'zip-bad',
+ 'Bad central entry error' );
+ }
+
+ function testTrailingBytes() {
+ $this->readZipAssertError( 'trail.zip', 'zip-bad',
+ 'Trailing bytes error' );
+ }
+
+ function testWrongCDStart() {
+ $this->readZipAssertError( 'wrong-cd-start-disk.zip', 'zip-unsupported',
+ 'Wrong CD start disk error' );
+ }
+
+
+ function testCentralDirectoryGap() {
+ $this->readZipAssertError( 'cd-gap.zip', 'zip-bad',
+ 'CD gap error' );
+ }
+
+ function testCentralDirectoryTruncated() {
+ $this->readZipAssertError( 'cd-truncated.zip', 'zip-bad',
+ 'CD truncated error (should hit unpack() overrun)' );
+ }
+
+ function testLooksLikeZip64() {
+ $this->readZipAssertError( 'looks-like-zip64.zip', 'zip-unsupported',
+ 'A file which looks like ZIP64 but isn\'t, should give error' );
+ }
+}
diff --git a/tests/phpunit/includes/api/ApiBlockTest.php b/tests/phpunit/includes/api/ApiBlockTest.php
new file mode 100644
index 00000000..227555eb
--- /dev/null
+++ b/tests/phpunit/includes/api/ApiBlockTest.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @group Database
+ */
+class ApiBlockTest extends ApiTestCase {
+
+ function setUp() {
+ parent::setUp();
+ $this->doLogin();
+ }
+
+ function getTokens() {
+ return $this->getTokenList( self::$users['sysop'] );
+ }
+
+ function addDBData() {
+ $user = User::newFromName( 'UTBlockee' );
+
+ if ( $user->getId() == 0 ) {
+ $user->addToDatabase();
+ $user->setPassword( 'UTBlockeePassword' );
+
+ $user->saveSettings();
+ }
+ }
+
+ function testMakeNormalBlock() {
+
+ $data = $this->getTokens();
+
+ $user = User::newFromName( 'UTBlockee' );
+
+ if ( !$user->getId() ) {
+ $this->markTestIncomplete( "The user UTBlockee does not exist" );
+ }
+
+ if( !isset( $data[0]['query']['pages'] ) ) {
+ $this->markTestIncomplete( "No block token found" );
+ }
+
+ $keys = array_keys( $data[0]['query']['pages'] );
+ $key = array_pop( $keys );
+ $pageinfo = $data[0]['query']['pages'][$key];
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'block',
+ 'user' => 'UTBlockee',
+ 'reason' => BlockTest::REASON,
+ 'token' => $pageinfo['blocktoken'] ), $data );
+
+ $block = Block::newFromTarget('UTBlockee');
+
+ $this->assertTrue( !is_null( $block ), 'Block is valid' );
+
+ $this->assertEquals( 'UTBlockee', (string)$block->getTarget() );
+ $this->assertEquals( 'Some reason', $block->mReason );
+ $this->assertEquals( 'infinity', $block->mExpiry );
+
+ }
+
+}
diff --git a/tests/phpunit/includes/api/ApiPurgeTest.php b/tests/phpunit/includes/api/ApiPurgeTest.php
new file mode 100644
index 00000000..db1563e9
--- /dev/null
+++ b/tests/phpunit/includes/api/ApiPurgeTest.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @group Database
+ */
+class ApiPurgeTest extends ApiTestCase {
+
+ function setUp() {
+ parent::setUp();
+ $this->doLogin();
+ }
+
+ function testPurgeMainPage() {
+
+ if ( !Title::newFromText( 'UTPage' )->exists() ) {
+ $this->markTestIncomplete( "The article [[UTPage]] does not exist" );
+ }
+
+ $somePage = mt_rand();
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'purge',
+ 'titles' => 'UTPage|' . $somePage . '|%5D' ) );
+
+ $this->assertArrayHasKey( 'purge', $data[0] );
+
+ $this->assertArrayHasKey( 0, $data[0]['purge'] );
+ $this->assertArrayHasKey( 'purged', $data[0]['purge'][0] );
+ $this->assertEquals( 'UTPage', $data[0]['purge'][0]['title'] );
+
+ $this->assertArrayHasKey( 1, $data[0]['purge'] );
+ $this->assertArrayHasKey( 'missing', $data[0]['purge'][1] );
+ $this->assertEquals( $somePage, $data[0]['purge'][1]['title'] );
+
+ $this->assertArrayHasKey( 2, $data[0]['purge'] );
+ $this->assertArrayHasKey( 'invalid', $data[0]['purge'][2] );
+ $this->assertEquals( '%5D', $data[0]['purge'][2]['title'] );
+
+ }
+
+}
diff --git a/tests/phpunit/includes/api/ApiQueryTest.php b/tests/phpunit/includes/api/ApiQueryTest.php
new file mode 100644
index 00000000..114eadf3
--- /dev/null
+++ b/tests/phpunit/includes/api/ApiQueryTest.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @group Database
+ */
+class ApiQueryTest extends ApiTestCase {
+
+ function setUp() {
+ parent::setUp();
+ $this->doLogin();
+ }
+
+ function testTitlesGetNormalized() {
+
+ global $wgMetaNamespace;
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'query',
+ 'titles' => 'Project:articleA|article_B' ) );
+
+
+ $this->assertArrayHasKey( 'query', $data[0] );
+ $this->assertArrayHasKey( 'normalized', $data[0]['query'] );
+
+ $this->assertEquals(
+ array(
+ 'from' => 'Project:articleA',
+ 'to' => $wgMetaNamespace . ':ArticleA'
+ ),
+ $data[0]['query']['normalized'][0]
+ );
+
+ $this->assertEquals(
+ array(
+ 'from' => 'article_B',
+ 'to' => 'Article B'
+ ),
+ $data[0]['query']['normalized'][1]
+ );
+
+ }
+
+ function testTitlesAreRejectedIfInvalid() {
+ $title = false;
+ while( !$title || Title::newFromText( $title )->exists() ) {
+ $title = md5( mt_rand( 0, 10000 ) + rand( 0, 999000 ) );
+ }
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'query',
+ 'titles' => $title . '|Talk:' ) );
+
+
+ $this->assertArrayHasKey( 'query', $data[0] );
+ $this->assertArrayHasKey( 'pages', $data[0]['query'] );
+ $this->assertEquals( 2, count( $data[0]['query']['pages'] ) );
+
+ $this->assertArrayHasKey( -2, $data[0]['query']['pages'] );
+ $this->assertArrayHasKey( -1, $data[0]['query']['pages'] );
+
+ $this->assertArrayHasKey( 'missing', $data[0]['query']['pages'][-2] );
+ $this->assertArrayHasKey( 'invalid', $data[0]['query']['pages'][-1] );
+
+
+ }
+
+}
diff --git a/tests/phpunit/includes/api/ApiTest.php b/tests/phpunit/includes/api/ApiTest.php
new file mode 100644
index 00000000..a587e6b1
--- /dev/null
+++ b/tests/phpunit/includes/api/ApiTest.php
@@ -0,0 +1,277 @@
+<?php
+
+/**
+ * @group Database
+ */
+class ApiTest extends ApiTestCase {
+
+ function testRequireOnlyOneParameterDefault() {
+ $mock = new MockApi();
+
+ $this->assertEquals(
+ null, $mock->requireOnlyOneParameter( array( "filename" => "foo.txt",
+ "enablechunks" => false ), "filename", "enablechunks" ) );
+ }
+
+ /**
+ * @expectedException UsageException
+ */
+ function testRequireOnlyOneParameterZero() {
+ $mock = new MockApi();
+
+ $this->assertEquals(
+ null, $mock->requireOnlyOneParameter( array( "filename" => "foo.txt",
+ "enablechunks" => 0 ), "filename", "enablechunks" ) );
+ }
+
+ /**
+ * @expectedException UsageException
+ */
+ function testRequireOnlyOneParameterTrue() {
+ $mock = new MockApi();
+
+ $this->assertEquals(
+ null, $mock->requireOnlyOneParameter( array( "filename" => "foo.txt",
+ "enablechunks" => true ), "filename", "enablechunks" ) );
+ }
+
+ /**
+ * Test that the API will accept a FauxRequest and execute. The help action
+ * (default) throws a UsageException. Just validate we're getting proper XML
+ *
+ * @expectedException UsageException
+ */
+ function testApi() {
+
+ $api = new ApiMain(
+ new FauxRequest( array( 'action' => 'help', 'format' => 'xml' ) )
+ );
+ $api->execute();
+ $api->getPrinter()->setBufferResult( true );
+ $api->printResult( false );
+ $resp = $api->getPrinter()->getBuffer();
+
+ libxml_use_internal_errors( true );
+ $sxe = simplexml_load_string( $resp );
+ $this->assertNotInternalType( "bool", $sxe );
+ $this->assertThat( $sxe, $this->isInstanceOf( "SimpleXMLElement" ) );
+ }
+
+ /**
+ * Test result of attempted login with an empty username
+ */
+ function testApiLoginNoName() {
+ $data = $this->doApiRequest( array( 'action' => 'login',
+ 'lgname' => '', 'lgpassword' => self::$users['sysop']->password,
+ ) );
+ $this->assertEquals( 'NoName', $data[0]['login']['result'] );
+ }
+
+ function testApiLoginBadPass() {
+ global $wgServer;
+
+ $user = self::$users['sysop'];
+ $user->user->logOut();
+
+ if ( !isset( $wgServer ) ) {
+ $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
+ }
+ $ret = $this->doApiRequest( array(
+ "action" => "login",
+ "lgname" => $user->username,
+ "lgpassword" => "bad",
+ )
+ );
+
+ $result = $ret[0];
+
+ $this->assertNotInternalType( "bool", $result );
+ $a = $result["login"]["result"];
+ $this->assertEquals( "NeedToken", $a );
+
+ $token = $result["login"]["token"];
+
+ $ret = $this->doApiRequest( array(
+ "action" => "login",
+ "lgtoken" => $token,
+ "lgname" => $user->username,
+ "lgpassword" => "badnowayinhell",
+ )
+ );
+
+ $result = $ret[0];
+
+ $this->assertNotInternalType( "bool", $result );
+ $a = $result["login"]["result"];
+
+ $this->assertEquals( "WrongPass", $a );
+ }
+
+ function testApiLoginGoodPass() {
+ global $wgServer;
+
+ if ( !isset( $wgServer ) ) {
+ $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
+ }
+
+ $user = self::$users['sysop'];
+ $user->user->logOut();
+
+ $ret = $this->doApiRequest( array(
+ "action" => "login",
+ "lgname" => $user->username,
+ "lgpassword" => $user->password,
+ )
+ );
+
+ $result = $ret[0];
+ $this->assertNotInternalType( "bool", $result );
+ $this->assertNotInternalType( "null", $result["login"] );
+
+ $a = $result["login"]["result"];
+ $this->assertEquals( "NeedToken", $a );
+ $token = $result["login"]["token"];
+
+ $ret = $this->doApiRequest( array(
+ "action" => "login",
+ "lgtoken" => $token,
+ "lgname" => $user->username,
+ "lgpassword" => $user->password,
+ )
+ );
+
+ $result = $ret[0];
+
+ $this->assertNotInternalType( "bool", $result );
+ $a = $result["login"]["result"];
+
+ $this->assertEquals( "Success", $a );
+ }
+
+ function testApiGotCookie() {
+ $this->markTestIncomplete( "The server can't do external HTTP requests, and the internal one won't give cookies" );
+
+ global $wgServer, $wgScriptPath;
+
+ if ( !isset( $wgServer ) ) {
+ $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
+ }
+ $user = self::$users['sysop'];
+
+ $req = MWHttpRequest::factory( self::$apiUrl . "?action=login&format=xml",
+ array( "method" => "POST",
+ "postData" => array(
+ "lgname" => $user->username,
+ "lgpassword" => $user->password ) ) );
+ $req->execute();
+
+ libxml_use_internal_errors( true );
+ $sxe = simplexml_load_string( $req->getContent() );
+ $this->assertNotInternalType( "bool", $sxe );
+ $this->assertThat( $sxe, $this->isInstanceOf( "SimpleXMLElement" ) );
+ $this->assertNotInternalType( "null", $sxe->login[0] );
+
+ $a = $sxe->login[0]->attributes()->result[0];
+ $this->assertEquals( ' result="NeedToken"', $a->asXML() );
+ $token = (string)$sxe->login[0]->attributes()->token;
+
+ $req->setData( array(
+ "lgtoken" => $token,
+ "lgname" => $user->username,
+ "lgpassword" => $user->password ) );
+ $req->execute();
+
+ $cj = $req->getCookieJar();
+ $serverName = parse_url( $wgServer, PHP_URL_HOST );
+ $this->assertNotEquals( false, $serverName );
+ $serializedCookie = $cj->serializeToHttpRequest( $wgScriptPath, $serverName );
+ $this->assertNotEquals( '', $serializedCookie );
+ $this->assertRegexp( '/_session=[^;]*; .*UserID=[0-9]*; .*UserName=' . $user->userName . '; .*Token=/', $serializedCookie );
+
+ return $cj;
+ }
+
+ /**
+ * @depends testApiGotCookie
+ */
+ function testApiListPages( CookieJar $cj ) {
+ $this->markTestIncomplete( "Not done with this yet" );
+ global $wgServer;
+
+ if ( $wgServer == "http://localhost" ) {
+ $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
+ }
+ $req = MWHttpRequest::factory( self::$apiUrl . "?action=query&format=xml&prop=revisions&" .
+ "titles=Main%20Page&rvprop=timestamp|user|comment|content" );
+ $req->setCookieJar( $cj );
+ $req->execute();
+ libxml_use_internal_errors( true );
+ $sxe = simplexml_load_string( $req->getContent() );
+ $this->assertNotInternalType( "bool", $sxe );
+ $this->assertThat( $sxe, $this->isInstanceOf( "SimpleXMLElement" ) );
+ $a = $sxe->query[0]->pages[0]->page[0]->attributes();
+ }
+
+ function testRunLogin() {
+ $sysopUser = self::$users['sysop'];
+ $data = $this->doApiRequest( array(
+ 'action' => 'login',
+ 'lgname' => $sysopUser->username,
+ 'lgpassword' => $sysopUser->password ) );
+
+ $this->assertArrayHasKey( "login", $data[0] );
+ $this->assertArrayHasKey( "result", $data[0]['login'] );
+ $this->assertEquals( "NeedToken", $data[0]['login']['result'] );
+ $token = $data[0]['login']['token'];
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'login',
+ "lgtoken" => $token,
+ "lgname" => $sysopUser->username,
+ "lgpassword" => $sysopUser->password ), $data );
+
+ $this->assertArrayHasKey( "login", $data[0] );
+ $this->assertArrayHasKey( "result", $data[0]['login'] );
+ $this->assertEquals( "Success", $data[0]['login']['result'] );
+ $this->assertArrayHasKey( 'lgtoken', $data[0]['login'] );
+
+ return $data;
+ }
+
+ function testGettingToken() {
+ foreach ( self::$users as $user ) {
+ $this->runTokenTest( $user );
+ }
+ }
+
+ function runTokenTest( $user ) {
+
+ $data = $this->getTokenList( $user );
+
+ $this->assertArrayHasKey( 'query', $data[0] );
+ $this->assertArrayHasKey( 'pages', $data[0]['query'] );
+ $keys = array_keys( $data[0]['query']['pages'] );
+ $key = array_pop( $keys );
+
+ $rights = $user->user->getRights();
+
+ $this->assertArrayHasKey( $key, $data[0]['query']['pages'] );
+ $this->assertArrayHasKey( 'edittoken', $data[0]['query']['pages'][$key] );
+ $this->assertArrayHasKey( 'movetoken', $data[0]['query']['pages'][$key] );
+
+ if ( isset( $rights['delete'] ) ) {
+ $this->assertArrayHasKey( 'deletetoken', $data[0]['query']['pages'][$key] );
+ }
+
+ if ( isset( $rights['block'] ) ) {
+ $this->assertArrayHasKey( 'blocktoken', $data[0]['query']['pages'][$key] );
+ $this->assertArrayHasKey( 'unblocktoken', $data[0]['query']['pages'][$key] );
+ }
+
+ if ( isset( $rights['protect'] ) ) {
+ $this->assertArrayHasKey( 'protecttoken', $data[0]['query']['pages'][$key] );
+ }
+
+ return $data;
+ }
+}
diff --git a/tests/phpunit/includes/api/ApiTestCase.php b/tests/phpunit/includes/api/ApiTestCase.php
new file mode 100644
index 00000000..2917c880
--- /dev/null
+++ b/tests/phpunit/includes/api/ApiTestCase.php
@@ -0,0 +1,139 @@
+<?php
+
+abstract class ApiTestCase extends MediaWikiLangTestCase {
+ /**
+ * @var Array of ApiTestUser
+ */
+ public static $users;
+ protected static $apiUrl;
+
+ function setUp() {
+ global $wgContLang, $wgAuth, $wgMemc, $wgRequest, $wgUser, $wgServer;
+
+ parent::setUp();
+ self::$apiUrl = $wgServer . wfScript( 'api' );
+ $wgMemc = new EmptyBagOStuff();
+ $wgContLang = Language::factory( 'en' );
+ $wgAuth = new StubObject( 'wgAuth', 'AuthPlugin' );
+ $wgRequest = new FauxRequest( array() );
+
+ self::$users = array(
+ 'sysop' => new ApiTestUser(
+ 'Apitestsysop',
+ 'Api Test Sysop',
+ 'api_test_sysop@sample.com',
+ array( 'sysop' )
+ ),
+ 'uploader' => new ApiTestUser(
+ 'Apitestuser',
+ 'Api Test User',
+ 'api_test_user@sample.com',
+ array()
+ )
+ );
+
+ $wgUser = self::$users['sysop']->user;
+
+ }
+
+ protected function doApiRequest( $params, $session = null, $appendModule = false ) {
+ if ( is_null( $session ) ) {
+ $session = array();
+ }
+
+ $request = new FauxRequest( $params, true, $session );
+ $module = new ApiMain( $request, true );
+ $module->execute();
+
+ $results = array( $module->getResultData(), $request, $request->getSessionArray() );
+ if( $appendModule ) {
+ $results[] = $module;
+ }
+
+ return $results;
+ }
+
+ /**
+ * Add an edit token to the API request
+ * This is cheating a bit -- we grab a token in the correct format and then add it to the pseudo-session and to the
+ * request, without actually requesting a "real" edit token
+ * @param $params: key-value API params
+ * @param $session: session array
+ */
+ protected function doApiRequestWithToken( $params, $session ) {
+ if ( $session['wsToken'] ) {
+ // add edit token to fake session
+ $session['wsEditToken'] = $session['wsToken'];
+ // add token to request parameters
+ $params['token'] = md5( $session['wsToken'] ) . User::EDIT_TOKEN_SUFFIX;
+ return $this->doApiRequest( $params, $session );
+ } else {
+ throw new Exception( "request data not in right format" );
+ }
+ }
+
+ protected function doLogin() {
+ $data = $this->doApiRequest( array(
+ 'action' => 'login',
+ 'lgname' => self::$users['sysop']->username,
+ 'lgpassword' => self::$users['sysop']->password ) );
+
+ $token = $data[0]['login']['token'];
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'login',
+ 'lgtoken' => $token,
+ 'lgname' => self::$users['sysop']->username,
+ 'lgpassword' => self::$users['sysop']->password
+ ), $data );
+
+ return $data;
+ }
+
+ protected function getTokenList( $user ) {
+ $GLOBALS['wgUser'] = $user->user;
+ $data = $this->doApiRequest( array(
+ 'action' => 'query',
+ 'titles' => 'Main Page',
+ 'intoken' => 'edit|delete|protect|move|block|unblock',
+ 'prop' => 'info' ) );
+ return $data;
+ }
+}
+
+class UserWrapper {
+ public $userName, $password, $user;
+
+ public function __construct( $userName, $password, $group = '' ) {
+ $this->userName = $userName;
+ $this->password = $password;
+
+ $this->user = User::newFromName( $this->userName );
+ if ( !$this->user->getID() ) {
+ $this->user = User::createNew( $this->userName, array(
+ "email" => "test@example.com",
+ "real_name" => "Test User" ) );
+ }
+ $this->user->setPassword( $this->password );
+
+ if ( $group !== '' ) {
+ $this->user->addGroup( $group );
+ }
+ $this->user->saveSettings();
+ }
+}
+
+class MockApi extends ApiBase {
+ public function execute() { }
+ public function getVersion() { }
+
+ public function __construct() { }
+
+ public function getAllowedParams() {
+ return array(
+ 'filename' => null,
+ 'enablechunks' => false,
+ 'sessionkey' => null,
+ );
+ }
+}
diff --git a/tests/phpunit/includes/api/ApiTestCaseUpload.php b/tests/phpunit/includes/api/ApiTestCaseUpload.php
new file mode 100644
index 00000000..e51e7214
--- /dev/null
+++ b/tests/phpunit/includes/api/ApiTestCaseUpload.php
@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * * Abstract class to support upload tests
+ */
+
+abstract class ApiTestCaseUpload extends ApiTestCase {
+ /**
+ * Fixture -- run before every test
+ */
+ public function setUp() {
+ global $wgEnableUploads, $wgEnableAPI;
+ parent::setUp();
+
+ $wgEnableUploads = true;
+ $wgEnableAPI = true;
+ wfSetupSession();
+
+ $this->clearFakeUploads();
+ }
+
+ /**
+ * Helper function -- remove files and associated articles by Title
+ * @param $title Title: title to be removed
+ */
+ public function deleteFileByTitle( $title ) {
+ if ( $title->exists() ) {
+ $file = wfFindFile( $title, array( 'ignoreRedirect' => true ) );
+ $noOldArchive = ""; // yes this really needs to be set this way
+ $comment = "removing for test";
+ $restrictDeletedVersions = false;
+ $status = FileDeleteForm::doDelete( $title, $file, $noOldArchive, $comment, $restrictDeletedVersions );
+ if ( !$status->isGood() ) {
+ return false;
+ }
+ $article = new Article( $title );
+ $article->doDeleteArticle( "removing for test" );
+
+ // see if it now doesn't exist; reload
+ $title = Title::newFromText( $title->getText(), NS_FILE );
+ }
+ return ! ( $title && $title instanceof Title && $title->exists() );
+ }
+
+ /**
+ * Helper function -- remove files and associated articles with a particular filename
+ * @param $fileName String: filename to be removed
+ */
+ public function deleteFileByFileName( $fileName ) {
+ return $this->deleteFileByTitle( Title::newFromText( $fileName, NS_FILE ) );
+ }
+
+
+ /**
+ * Helper function -- given a file on the filesystem, find matching content in the db (and associated articles) and remove them.
+ * @param $filePath String: path to file on the filesystem
+ */
+ public function deleteFileByContent( $filePath ) {
+ $hash = File::sha1Base36( $filePath );
+ $dupes = RepoGroup::singleton()->findBySha1( $hash );
+ $success = true;
+ foreach ( $dupes as $dupe ) {
+ $success &= $this->deleteFileByTitle( $dupe->getTitle() );
+ }
+ return $success;
+ }
+
+ /**
+ * Fake an upload by dumping the file into temp space, and adding info to $_FILES.
+ * (This is what PHP would normally do).
+ * @param $fieldName String: name this would have in the upload form
+ * @param $fileName String: name to title this
+ * @param $type String: mime type
+ * @param $filePath String: path where to find file contents
+ */
+ function fakeUploadFile( $fieldName, $fileName, $type, $filePath ) {
+ $tmpName = tempnam( wfTempDir(), "" );
+ if ( !file_exists( $filePath ) ) {
+ throw new Exception( "$filePath doesn't exist!" );
+ };
+
+ if ( !copy( $filePath, $tmpName ) ) {
+ throw new Exception( "couldn't copy $filePath to $tmpName" );
+ }
+
+ clearstatcache();
+ $size = filesize( $tmpName );
+ if ( $size === false ) {
+ throw new Exception( "couldn't stat $tmpName" );
+ }
+
+ $_FILES[ $fieldName ] = array(
+ 'name' => $fileName,
+ 'type' => $type,
+ 'tmp_name' => $tmpName,
+ 'size' => $size,
+ 'error' => null
+ );
+
+ return true;
+
+ }
+
+ /**
+ * Remove traces of previous fake uploads
+ */
+ function clearFakeUploads() {
+ $_FILES = array();
+ }
+
+
+
+
+}
diff --git a/tests/phpunit/includes/api/ApiTestUser.php b/tests/phpunit/includes/api/ApiTestUser.php
new file mode 100644
index 00000000..df60682f
--- /dev/null
+++ b/tests/phpunit/includes/api/ApiTestUser.php
@@ -0,0 +1,59 @@
+<?php
+
+/* Wraps the user object, so we can also retain full access to properties like password if we log in via the API */
+class ApiTestUser {
+ public $username;
+ public $password;
+ public $email;
+ public $groups;
+ public $user;
+
+ function __construct( $username, $realname = 'Real Name', $email = 'sample@sample.com', $groups = array() ) {
+ $this->username = $username;
+ $this->realname = $realname;
+ $this->email = $email;
+ $this->groups = $groups;
+
+ // don't allow user to hardcode or select passwords -- people sometimes run tests
+ // on live wikis. Sometimes we create sysop users in these tests. A sysop user with
+ // a known password would be a Bad Thing.
+ $this->password = User::randomPassword();
+
+ $this->user = User::newFromName( $this->username );
+ $this->user->load();
+
+ // In an ideal world we'd have a new wiki (or mock data store) for every single test.
+ // But for now, we just need to create or update the user with the desired properties.
+ // we particularly need the new password, since we just generated it randomly.
+ // In core MediaWiki, there is no functionality to delete users, so this is the best we can do.
+ if ( !$this->user->getID() ) {
+ // create the user
+ $this->user = User::createNew(
+ $this->username, array(
+ "email" => $this->email,
+ "real_name" => $this->realname
+ )
+ );
+ if ( !$this->user ) {
+ throw new Exception( "error creating user" );
+ }
+ }
+
+ // update the user to use the new random password and other details
+ $this->user->setPassword( $this->password );
+ $this->user->setEmail( $this->email );
+ $this->user->setRealName( $this->realname );
+ // remove all groups, replace with any groups specified
+ foreach ( $this->user->getGroups() as $group ) {
+ $this->user->removeGroup( $group );
+ }
+ if ( count( $this->groups ) ) {
+ foreach ( $this->groups as $group ) {
+ $this->user->addGroup( $group );
+ }
+ }
+ $this->user->saveSettings();
+
+ }
+
+}
diff --git a/tests/phpunit/includes/api/ApiUploadTest.php b/tests/phpunit/includes/api/ApiUploadTest.php
new file mode 100644
index 00000000..5c929784
--- /dev/null
+++ b/tests/phpunit/includes/api/ApiUploadTest.php
@@ -0,0 +1,433 @@
+<?php
+
+/**
+ * @group Database
+ */
+
+/**
+ * n.b. Ensure that you can write to the images/ directory as the
+ * user that will run tests.
+ */
+
+// Note for reviewers: this intentionally duplicates functionality already in "ApiSetup" and so on.
+// This framework works better IMO and has less strangeness (such as test cases inheriting from "ApiSetup"...)
+// (and in the case of the other Upload tests, this flat out just actually works... )
+
+// TODO: port the other Upload tests, and other API tests to this framework
+
+require_once( 'ApiTestCaseUpload.php' );
+
+/**
+ * @group Database
+ *
+ * This is pretty sucky... needs to be prettified.
+ */
+class ApiUploadTest extends ApiTestCaseUpload {
+
+ /**
+ * Testing login
+ * XXX this is a funny way of getting session context
+ */
+ function testLogin() {
+ $user = self::$users['uploader'];
+
+ $params = array(
+ 'action' => 'login',
+ 'lgname' => $user->username,
+ 'lgpassword' => $user->password
+ );
+ list( $result, , $session ) = $this->doApiRequest( $params );
+ $this->assertArrayHasKey( "login", $result );
+ $this->assertArrayHasKey( "result", $result['login'] );
+ $this->assertEquals( "NeedToken", $result['login']['result'] );
+ $token = $result['login']['token'];
+
+ $params = array(
+ 'action' => 'login',
+ 'lgtoken' => $token,
+ 'lgname' => $user->username,
+ 'lgpassword' => $user->password
+ );
+ list( $result, , $session ) = $this->doApiRequest( $params, $session );
+ $this->assertArrayHasKey( "login", $result );
+ $this->assertArrayHasKey( "result", $result['login'] );
+ $this->assertEquals( "Success", $result['login']['result'] );
+ $this->assertArrayHasKey( 'lgtoken', $result['login'] );
+
+ return $session;
+
+ }
+
+ /**
+ * @depends testLogin
+ */
+ public function testUploadRequiresToken( $session ) {
+ $exception = false;
+ try {
+ $this->doApiRequest( array(
+ 'action' => 'upload'
+ ) );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ $this->assertEquals( "The token parameter must be set", $e->getMessage() );
+ }
+ $this->assertTrue( $exception, "Got exception" );
+ }
+
+ /**
+ * @depends testLogin
+ */
+ public function testUploadMissingParams( $session ) {
+ global $wgUser;
+ $wgUser = self::$users['uploader']->user;
+
+ $exception = false;
+ try {
+ $this->doApiRequestWithToken( array(
+ 'action' => 'upload',
+ ), $session );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ $this->assertEquals( "One of the parameters filekey, file, url, statuskey is required",
+ $e->getMessage() );
+ }
+ $this->assertTrue( $exception, "Got exception" );
+ }
+
+
+ /**
+ * @depends testLogin
+ */
+ public function testUpload( $session ) {
+ global $wgUser;
+ $wgUser = self::$users['uploader']->user;
+
+ $extension = 'png';
+ $mimeType = 'image/png';
+
+ try {
+ $randomImageGenerator = new RandomImageGenerator();
+ }
+ catch ( Exception $e ) {
+ $this->markTestIncomplete( $e->getMessage() );
+ }
+
+ $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() );
+ $filePath = $filePaths[0];
+ $fileSize = filesize( $filePath );
+ $fileName = basename( $filePath );
+
+ $this->deleteFileByFileName( $fileName );
+ $this->deleteFileByContent( $filePath );
+
+
+ if (! $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) {
+ $this->markTestIncomplete( "Couldn't upload file!\n" );
+ }
+
+ $params = array(
+ 'action' => 'upload',
+ 'filename' => $fileName,
+ 'file' => 'dummy content',
+ 'comment' => 'dummy comment',
+ 'text' => "This is the page text for $fileName",
+ );
+
+ $exception = false;
+ try {
+ list( $result, , ) = $this->doApiRequestWithToken( $params, $session );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ }
+ $this->assertTrue( isset( $result['upload'] ) );
+ $this->assertEquals( 'Success', $result['upload']['result'] );
+ $this->assertEquals( $fileSize, ( int )$result['upload']['imageinfo']['size'] );
+ $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
+ $this->assertFalse( $exception );
+
+ // clean up
+ $this->deleteFileByFilename( $fileName );
+ unlink( $filePath );
+ }
+
+
+ /**
+ * @depends testLogin
+ */
+ public function testUploadZeroLength( $session ) {
+ global $wgUser;
+ $wgUser = self::$users['uploader']->user;
+
+ $mimeType = 'image/png';
+
+ $filePath = tempnam( wfTempDir(), "" );
+ $fileName = "apiTestUploadZeroLength.png";
+
+ $this->deleteFileByFileName( $fileName );
+
+ if (! $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) {
+ $this->markTestIncomplete( "Couldn't upload file!\n" );
+ }
+
+ $params = array(
+ 'action' => 'upload',
+ 'filename' => $fileName,
+ 'file' => 'dummy content',
+ 'comment' => 'dummy comment',
+ 'text' => "This is the page text for $fileName",
+ );
+
+ $exception = false;
+ try {
+ $this->doApiRequestWithToken( $params, $session );
+ } catch ( UsageException $e ) {
+ $this->assertContains( 'The file you submitted was empty', $e->getMessage() );
+ $exception = true;
+ }
+ $this->assertTrue( $exception );
+
+ // clean up
+ $this->deleteFileByFilename( $fileName );
+ unlink( $filePath );
+ }
+
+
+ /**
+ * @depends testLogin
+ */
+ public function testUploadSameFileName( $session ) {
+ global $wgUser;
+ $wgUser = self::$users['uploader']->user;
+
+ $extension = 'png';
+ $mimeType = 'image/png';
+
+ try {
+ $randomImageGenerator = new RandomImageGenerator();
+ }
+ catch ( Exception $e ) {
+ $this->markTestIncomplete( $e->getMessage() );
+ }
+
+ $filePaths = $randomImageGenerator->writeImages( 2, $extension, wfTempDir() );
+ // we'll reuse this filename
+ $fileName = basename( $filePaths[0] );
+
+ // clear any other files with the same name
+ $this->deleteFileByFileName( $fileName );
+
+ // we reuse these params
+ $params = array(
+ 'action' => 'upload',
+ 'filename' => $fileName,
+ 'file' => 'dummy content',
+ 'comment' => 'dummy comment',
+ 'text' => "This is the page text for $fileName",
+ );
+
+ // first upload .... should succeed
+
+ if (! $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[0] ) ) {
+ $this->markTestIncomplete( "Couldn't upload file!\n" );
+ }
+
+ $exception = false;
+ try {
+ list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ }
+ $this->assertTrue( isset( $result['upload'] ) );
+ $this->assertEquals( 'Success', $result['upload']['result'] );
+ $this->assertFalse( $exception );
+
+ // second upload with the same name (but different content)
+
+ if (! $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[1] ) ) {
+ $this->markTestIncomplete( "Couldn't upload file!\n" );
+ }
+
+ $exception = false;
+ try {
+ list( $result, , ) = $this->doApiRequestWithToken( $params, $session );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ }
+ $this->assertTrue( isset( $result['upload'] ) );
+ $this->assertEquals( 'Warning', $result['upload']['result'] );
+ $this->assertTrue( isset( $result['upload']['warnings'] ) );
+ $this->assertTrue( isset( $result['upload']['warnings']['exists'] ) );
+ $this->assertFalse( $exception );
+
+ // clean up
+ $this->deleteFileByFilename( $fileName );
+ unlink( $filePaths[0] );
+ unlink( $filePaths[1] );
+ }
+
+
+ /**
+ * @depends testLogin
+ */
+ public function testUploadSameContent( $session ) {
+ global $wgUser;
+ $wgUser = self::$users['uploader']->user;
+
+ $extension = 'png';
+ $mimeType = 'image/png';
+
+ try {
+ $randomImageGenerator = new RandomImageGenerator();
+ }
+ catch ( Exception $e ) {
+ $this->markTestIncomplete( $e->getMessage() );
+ }
+ $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() );
+ $fileNames[0] = basename( $filePaths[0] );
+ $fileNames[1] = "SameContentAs" . $fileNames[0];
+
+ // clear any other files with the same name or content
+ $this->deleteFileByContent( $filePaths[0] );
+ $this->deleteFileByFileName( $fileNames[0] );
+ $this->deleteFileByFileName( $fileNames[1] );
+
+ // first upload .... should succeed
+
+ $params = array(
+ 'action' => 'upload',
+ 'filename' => $fileNames[0],
+ 'file' => 'dummy content',
+ 'comment' => 'dummy comment',
+ 'text' => "This is the page text for " . $fileNames[0],
+ );
+
+ if (! $this->fakeUploadFile( 'file', $fileNames[0], $mimeType, $filePaths[0] ) ) {
+ $this->markTestIncomplete( "Couldn't upload file!\n" );
+ }
+
+ $exception = false;
+ try {
+ list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ }
+ $this->assertTrue( isset( $result['upload'] ) );
+ $this->assertEquals( 'Success', $result['upload']['result'] );
+ $this->assertFalse( $exception );
+
+
+ // second upload with the same content (but different name)
+
+ if (! $this->fakeUploadFile( 'file', $fileNames[1], $mimeType, $filePaths[0] ) ) {
+ $this->markTestIncomplete( "Couldn't upload file!\n" );
+ }
+
+ $params = array(
+ 'action' => 'upload',
+ 'filename' => $fileNames[1],
+ 'file' => 'dummy content',
+ 'comment' => 'dummy comment',
+ 'text' => "This is the page text for " . $fileNames[1],
+ );
+
+ $exception = false;
+ try {
+ list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ }
+ $this->assertTrue( isset( $result['upload'] ) );
+ $this->assertEquals( 'Warning', $result['upload']['result'] );
+ $this->assertTrue( isset( $result['upload']['warnings'] ) );
+ $this->assertTrue( isset( $result['upload']['warnings']['duplicate'] ) );
+ $this->assertFalse( $exception );
+
+ // clean up
+ $this->deleteFileByFilename( $fileNames[0] );
+ $this->deleteFileByFilename( $fileNames[1] );
+ unlink( $filePaths[0] );
+ }
+
+
+ /**
+ * @depends testLogin
+ */
+ public function testUploadStash( $session ) {
+ global $wgUser;
+ $wgUser = self::$users['uploader']->user;
+
+ $extension = 'png';
+ $mimeType = 'image/png';
+
+ try {
+ $randomImageGenerator = new RandomImageGenerator();
+ }
+ catch ( Exception $e ) {
+ $this->markTestIncomplete( $e->getMessage() );
+ }
+
+ $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() );
+ $filePath = $filePaths[0];
+ $fileSize = filesize( $filePath );
+ $fileName = basename( $filePath );
+
+ $this->deleteFileByFileName( $fileName );
+ $this->deleteFileByContent( $filePath );
+
+ if (! $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) {
+ $this->markTestIncomplete( "Couldn't upload file!\n" );
+ }
+
+ $params = array(
+ 'action' => 'upload',
+ 'stash' => 1,
+ 'filename' => $fileName,
+ 'file' => 'dummy content',
+ 'comment' => 'dummy comment',
+ 'text' => "This is the page text for $fileName",
+ );
+
+ $exception = false;
+ try {
+ list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ }
+ $this->assertFalse( $exception );
+ $this->assertTrue( isset( $result['upload'] ) );
+ $this->assertEquals( 'Success', $result['upload']['result'] );
+ $this->assertEquals( $fileSize, ( int )$result['upload']['imageinfo']['size'] );
+ $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
+ $this->assertTrue( isset( $result['upload']['filekey'] ) );
+ $this->assertEquals( $result['upload']['sessionkey'], $result['upload']['filekey'] );
+ $filekey = $result['upload']['filekey'];
+
+ // it should be visible from Special:UploadStash
+ // XXX ...but how to test this, with a fake WebRequest with the session?
+
+ // now we should try to release the file from stash
+ $params = array(
+ 'action' => 'upload',
+ 'filekey' => $filekey,
+ 'filename' => $fileName,
+ 'comment' => 'dummy comment',
+ 'text' => "This is the page text for $fileName, altered",
+ );
+
+ $this->clearFakeUploads();
+ $exception = false;
+ try {
+ list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ }
+ $this->assertTrue( isset( $result['upload'] ) );
+ $this->assertEquals( 'Success', $result['upload']['result'] );
+ $this->assertFalse( $exception );
+
+ // clean up
+ $this->deleteFileByFilename( $fileName );
+ unlink( $filePath );
+ }
+}
+
diff --git a/tests/phpunit/includes/api/ApiWatchTest.php b/tests/phpunit/includes/api/ApiWatchTest.php
new file mode 100644
index 00000000..3c7ff304
--- /dev/null
+++ b/tests/phpunit/includes/api/ApiWatchTest.php
@@ -0,0 +1,179 @@
+<?php
+
+/**
+ * @group Database
+ * @todo This test suite is severly broken and need a full review
+ */
+class ApiWatchTest extends ApiTestCase {
+
+ function setUp() {
+ parent::setUp();
+ $this->doLogin();
+ }
+
+ function getTokens() {
+ return $this->getTokenList( self::$users['sysop'] );
+ }
+
+ /**
+ * @group Broken
+ */
+ function testWatchEdit() {
+
+ $data = $this->getTokens();
+
+ $keys = array_keys( $data[0]['query']['pages'] );
+ $key = array_pop( $keys );
+ $pageinfo = $data[0]['query']['pages'][$key];
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'edit',
+ 'title' => 'UTPage',
+ 'text' => 'new text',
+ 'token' => $pageinfo['edittoken'],
+ 'watchlist' => 'watch' ), $data );
+ $this->assertArrayHasKey( 'edit', $data[0] );
+ $this->assertArrayHasKey( 'result', $data[0]['edit'] );
+ $this->assertEquals( 'Success', $data[0]['edit']['result'] );
+
+ return $data;
+ }
+
+ /**
+ * @depends testWatchEdit
+ */
+ function testWatchClear() {
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'query',
+ 'list' => 'watchlist' ), $data );
+
+ if ( isset( $data[0]['query']['watchlist'] ) ) {
+ $wl = $data[0]['query']['watchlist'];
+
+ foreach ( $wl as $page ) {
+ $data = $this->doApiRequest( array(
+ 'action' => 'watch',
+ 'title' => $page['title'],
+ 'unwatch' => true ), $data );
+ }
+ }
+ $data = $this->doApiRequest( array(
+ 'action' => 'query',
+ 'list' => 'watchlist' ), $data );
+ $this->assertArrayHasKey( 'query', $data[0] );
+ $this->assertArrayHasKey( 'watchlist', $data[0]['query'] );
+ $this->assertEquals( 0, count( $data[0]['query']['watchlist'] ) );
+
+ return $data;
+ }
+
+ /**
+ * @group Broken
+ */
+ function testWatchProtect() {
+
+ $data = $this->getTokens();
+
+ $keys = array_keys( $data[0]['query']['pages'] );
+ $key = array_pop( $keys );
+ $pageinfo = $data[0]['query']['pages'][$key];
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'protect',
+ 'token' => $pageinfo['protecttoken'],
+ 'title' => 'UTPage',
+ 'protections' => 'edit=sysop',
+ 'watchlist' => 'unwatch' ), $data );
+
+ $this->assertArrayHasKey( 'protect', $data[0] );
+ $this->assertArrayHasKey( 'protections', $data[0]['protect'] );
+ $this->assertEquals( 1, count( $data[0]['protect']['protections'] ) );
+ $this->assertArrayHasKey( 'edit', $data[0]['protect']['protections'][0] );
+ }
+
+
+ function testGetRollbackToken() {
+
+ $data = $this->getTokens();
+
+ if ( !Title::newFromText( 'UTPage' )->exists() ) {
+ $this->markTestIncomplete( "The article [[UTPage]] does not exist" );
+ }
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'query',
+ 'prop' => 'revisions',
+ 'titles' => 'UTPage',
+ 'rvtoken' => 'rollback' ), $data );
+
+ $this->assertArrayHasKey( 'query', $data[0] );
+ $this->assertArrayHasKey( 'pages', $data[0]['query'] );
+ $keys = array_keys( $data[0]['query']['pages'] );
+ $key = array_pop( $keys );
+
+ if ( isset( $data[0]['query']['pages'][$key]['missing'] ) ) {
+ $this->markTestIncomplete( "Target page (UTPage) doesn't exist" );
+ }
+
+ $this->assertArrayHasKey( 'pageid', $data[0]['query']['pages'][$key] );
+ $this->assertArrayHasKey( 'revisions', $data[0]['query']['pages'][$key] );
+ $this->assertArrayHasKey( 0, $data[0]['query']['pages'][$key]['revisions'] );
+ $this->assertArrayHasKey( 'rollbacktoken', $data[0]['query']['pages'][$key]['revisions'][0] );
+
+ return $data;
+ }
+
+ /**
+ * @depends testGetRollbackToken
+ * @group Broken
+ */
+ function testWatchRollback( $data ) {
+ $keys = array_keys( $data[0]['query']['pages'] );
+ $key = array_pop( $keys );
+ $pageinfo = $data[0]['query']['pages'][$key]['revisions'][0];
+
+ try {
+ $data = $this->doApiRequest( array(
+ 'action' => 'rollback',
+ 'title' => 'UTPage',
+ 'user' => $pageinfo['user'],
+ 'token' => $pageinfo['rollbacktoken'],
+ 'watchlist' => 'watch' ), $data );
+ } catch( UsageException $ue ) {
+ if( $ue->getCodeString() == 'onlyauthor' ) {
+ $this->markTestIncomplete( "Only one author to 'UTPage', cannot test rollback" );
+ } else {
+ $this->fail( "Received error '" . $ue->getCodeString() . "'" );
+ }
+ }
+
+ $this->assertArrayHasKey( 'rollback', $data[0] );
+ $this->assertArrayHasKey( 'title', $data[0]['rollback'] );
+ }
+
+ /**
+ * @group Broken
+ */
+ function testWatchDelete() {
+
+ $data = $this->getTokens();
+
+ $keys = array_keys( $data[0]['query']['pages'] );
+ $key = array_pop( $keys );
+ $pageinfo = $data[0]['query']['pages'][$key];
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'delete',
+ 'token' => $pageinfo['deletetoken'],
+ 'title' => 'UTPage' ), $data );
+ $this->assertArrayHasKey( 'delete', $data[0] );
+ $this->assertArrayHasKey( 'title', $data[0]['delete'] );
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'query',
+ 'list' => 'watchlist' ), $data );
+
+ $this->markTestIncomplete( 'This test needs to verify the deleted article was added to the users watchlist' );
+ }
+}
diff --git a/tests/phpunit/includes/api/RandomImageGenerator.php b/tests/phpunit/includes/api/RandomImageGenerator.php
new file mode 100644
index 00000000..ae349978
--- /dev/null
+++ b/tests/phpunit/includes/api/RandomImageGenerator.php
@@ -0,0 +1,473 @@
+<?php
+
+/*
+ * RandomImageGenerator -- does what it says on the tin.
+ * Requires Imagick, the ImageMagick library for PHP, or the command line equivalent (usually 'convert').
+ *
+ * Because MediaWiki tests the uniqueness of media upload content, and filenames, it is sometimes useful to generate
+ * files that are guaranteed (or at least very likely) to be unique in both those ways.
+ * This generates a number of filenames with random names and random content (colored triangles)
+ *
+ * It is also useful to have fresh content because our tests currently run in a "destructive" mode, and don't create a fresh new wiki for each
+ * test run.
+ * Consequently, if we just had a few static files we kept re-uploading, we'd get lots of warnings about matching content or filenames,
+ * and even if we deleted those files, we'd get warnings about archived files.
+ *
+ * This can also be used with a cronjob to generate random files all the time -- I use it to have a constant, never ending supply when I'm
+ * testing interactively.
+ *
+ * @file
+ * @author Neil Kandalgaonkar <neilk@wikimedia.org>
+ */
+
+/**
+ * RandomImageGenerator: does what it says on the tin.
+ * Can fetch a random image, or also write a number of them to disk with random filenames.
+ */
+class RandomImageGenerator {
+
+ private $dictionaryFile;
+ private $minWidth = 400;
+ private $maxWidth = 800;
+ private $minHeight = 400;
+ private $maxHeight = 800;
+ private $shapesToDraw = 5;
+ private $imageWriteMethod;
+
+ /**
+ * Orientations: 0th row, 0th column, EXIF orientation code, rotation 2x2 matrix that is opposite of orientation
+ * n.b. we do not handle the 'flipped' orientations, which is why there is no entry for 2, 4, 5, or 7. Those
+ * seem to be rare in real images anyway
+ * (we also would need a non-symmetric shape for the images to test those, like a letter F)
+ */
+ private static $orientations = array(
+ array(
+ '0thRow' => 'top',
+ '0thCol' => 'left',
+ 'exifCode' => 1,
+ 'counterRotation' => array( array( 1, 0 ), array( 0, 1 ) )
+ ),
+ array(
+ '0thRow' => 'bottom',
+ '0thCol' => 'right',
+ 'exifCode' => 3,
+ 'counterRotation' => array( array( -1, 0 ), array( 0, -1 ) )
+ ),
+ array(
+ '0thRow' => 'right',
+ '0thCol' => 'top',
+ 'exifCode' => 6,
+ 'counterRotation' => array( array( 0, 1 ), array( 1, 0 ) )
+ ),
+ array(
+ '0thRow' => 'left',
+ '0thCol' => 'bottom',
+ 'exifCode' => 8,
+ 'counterRotation' => array( array( 0, -1 ), array( -1, 0 ) )
+ )
+ );
+
+
+ public function __construct( $options = array() ) {
+ foreach ( array( 'dictionaryFile', 'minWidth', 'minHeight', 'maxHeight', 'shapesToDraw' ) as $property ) {
+ if ( isset( $options[$property] ) ) {
+ $this->$property = $options[$property];
+ }
+ }
+
+ // find the dictionary file, to generate random names
+ if ( !isset( $this->dictionaryFile ) ) {
+ foreach ( array(
+ '/usr/share/dict/words',
+ '/usr/dict/words',
+ dirname( __FILE__ ) . '/words.txt' )
+ as $dictionaryFile ) {
+ if ( is_file( $dictionaryFile ) and is_readable( $dictionaryFile ) ) {
+ $this->dictionaryFile = $dictionaryFile;
+ break;
+ }
+ }
+ }
+ if ( !isset( $this->dictionaryFile ) ) {
+ throw new Exception( "RandomImageGenerator: dictionary file not found or not specified properly" );
+ }
+
+ if ( !class_exists( 'Imagick' ) ) {
+ throw new Exception( 'No Imagick extension' );
+ }
+ global $wgExiv2Command;
+ if ( !$wgExiv2Command || !is_executable( $wgExiv2Command ) ) {
+ throw new Exception( 'exiv2 not executable or $wgExiv2Command not set' );
+ }
+ }
+
+ /**
+ * Writes random images with random filenames to disk in the directory you specify, or current working directory
+ *
+ * @param $number Integer: number of filenames to write
+ * @param $format String: optional, must be understood by ImageMagick, such as 'jpg' or 'gif'
+ * @param $dir String: directory, optional (will default to current working directory)
+ * @return Array: filenames we just wrote
+ */
+ function writeImages( $number, $format = 'jpg', $dir = null ) {
+ $filenames = $this->getRandomFilenames( $number, $format, $dir );
+ $imageWriteMethod = $this->getImageWriteMethod( $format );
+ foreach( $filenames as $filename ) {
+ $this->{$imageWriteMethod}( $this->getImageSpec(), $format, $filename );
+ }
+ return $filenames;
+ }
+
+
+ /**
+ * Figure out how we write images. This is a factor of both format and the local system
+ * @param $format (a typical extension like 'svg', 'jpg', etc.)
+ */
+ function getImageWriteMethod( $format ) {
+ global $wgUseImageMagick, $wgImageMagickConvertCommand;
+ if ( $format === 'svg' ) {
+ return 'writeSvg';
+ } else {
+ // figure out how to write images
+ if ( class_exists( 'Imagick' ) ) {
+ return 'writeImageWithApi';
+ } elseif ( $wgUseImageMagick && $wgImageMagickConvertCommand && is_executable( $wgImageMagickConvertCommand ) ) {
+ return 'writeImageWithCommandLine';
+ }
+ }
+ throw new Exception( "RandomImageGenerator: could not find a suitable method to write images in '$format' format" );
+ }
+
+ /**
+ * Return a number of randomly-generated filenames
+ * Each filename uses two words randomly drawn from the dictionary, like elephantine_spatula.jpg
+ *
+ * @param $number Integer: of filenames to generate
+ * @param $extension String: optional, defaults to 'jpg'
+ * @param $dir String: optional, defaults to current working directory
+ * @return Array: of filenames
+ */
+ private function getRandomFilenames( $number, $extension = 'jpg', $dir = null ) {
+ if ( is_null( $dir ) ) {
+ $dir = getcwd();
+ }
+ $filenames = array();
+ foreach( $this->getRandomWordPairs( $number ) as $pair ) {
+ $basename = $pair[0] . '_' . $pair[1];
+ if ( !is_null( $extension ) ) {
+ $basename .= '.' . $extension;
+ }
+ $basename = preg_replace( '/\s+/', '', $basename );
+ $filenames[] = "$dir/$basename";
+ }
+
+ return $filenames;
+
+ }
+
+
+ /**
+ * Generate data representing an image of random size (within limits),
+ * consisting of randomly colored and sized upward pointing triangles against a random background color
+ * (This data is used in the writeImage* methods).
+ * @return {Mixed}
+ */
+ public function getImageSpec() {
+ $spec = array();
+
+ $spec['width'] = mt_rand( $this->minWidth, $this->maxWidth );
+ $spec['height'] = mt_rand( $this->minHeight, $this->maxHeight );
+ $spec['fill'] = $this->getRandomColor();
+
+ $diagonalLength = sqrt( pow( $spec['width'], 2 ) + pow( $spec['height'], 2 ) );
+
+ $draws = array();
+ for ( $i = 0; $i <= $this->shapesToDraw; $i++ ) {
+ $radius = mt_rand( 0, $diagonalLength / 4 );
+ if ( $radius == 0 ) {
+ continue;
+ }
+ $originX = mt_rand( -1 * $radius, $spec['width'] + $radius );
+ $originY = mt_rand( -1 * $radius, $spec['height'] + $radius );
+ $angle = mt_rand( 0, ( 3.141592/2 ) * $radius ) / $radius;
+ $legDeltaX = round( $radius * sin( $angle ) );
+ $legDeltaY = round( $radius * cos( $angle ) );
+
+ $draw = array();
+ $draw['fill'] = $this->getRandomColor();
+ $draw['shape'] = array(
+ array( 'x' => $originX, 'y' => $originY - $radius ),
+ array( 'x' => $originX + $legDeltaX, 'y' => $originY + $legDeltaY ),
+ array( 'x' => $originX - $legDeltaX, 'y' => $originY + $legDeltaY ),
+ array( 'x' => $originX, 'y' => $originY - $radius )
+ );
+ $draws[] = $draw;
+
+ }
+
+ $spec['draws'] = $draws;
+
+ return $spec;
+ }
+
+ /**
+ * Given array( array('x' => 10, 'y' => 20), array( 'x' => 30, y=> 5 ) )
+ * returns "10,20 30,5"
+ * Useful for SVG and imagemagick command line arguments
+ * @param $shape: Array of arrays, each array containing x & y keys mapped to numeric values
+ * @return string
+ */
+ static function shapePointsToString( $shape ) {
+ $points = array();
+ foreach ( $shape as $point ) {
+ $points[] = $point['x'] . ',' . $point['y'];
+ }
+ return join( " ", $points );
+ }
+
+ /**
+ * Based on image specification, write a very simple SVG file to disk.
+ * Ignores the background spec because transparency is cool. :)
+ * @param $spec: spec describing background and shapes to draw
+ * @param $format: file format to write (which is obviously always svg here)
+ * @param $filename: filename to write to
+ */
+ public function writeSvg( $spec, $format, $filename ) {
+ $svg = new SimpleXmlElement( '<svg/>' );
+ $svg->addAttribute( 'xmlns', 'http://www.w3.org/2000/svg' );
+ $svg->addAttribute( 'version', '1.1' );
+ $svg->addAttribute( 'width', $spec['width'] );
+ $svg->addAttribute( 'height', $spec['height'] );
+ $g = $svg->addChild( 'g' );
+ foreach ( $spec['draws'] as $drawSpec ) {
+ $shape = $g->addChild( 'polygon' );
+ $shape->addAttribute( 'fill', $drawSpec['fill'] );
+ $shape->addAttribute( 'points', self::shapePointsToString( $drawSpec['shape'] ) );
+ };
+ if ( ! $fh = fopen( $filename, 'w' ) ) {
+ throw new Exception( "couldn't open $filename for writing" );
+ }
+ fwrite( $fh, $svg->asXML() );
+ if ( !fclose($fh) ) {
+ throw new Exception( "couldn't close $filename" );
+ }
+ }
+
+ /**
+ * Based on an image specification, write such an image to disk, using Imagick PHP extension
+ * @param $spec: spec describing background and circles to draw
+ * @param $format: file format to write
+ * @param $filename: filename to write to
+ */
+ public function writeImageWithApi( $spec, $format, $filename ) {
+ // this is a hack because I can't get setImageOrientation() to work. See below.
+ global $wgExiv2Command;
+
+ $image = new Imagick();
+ /**
+ * If the format is 'jpg', will also add a random orientation -- the image will be drawn rotated with triangle points
+ * facing in some direction (0, 90, 180 or 270 degrees) and a countering rotation should turn the triangle points upward again
+ */
+ $orientation = self::$orientations[0]; // default is normal orientation
+ if ( $format == 'jpg' ) {
+ $orientation = self::$orientations[ array_rand( self::$orientations ) ];
+ $spec = self::rotateImageSpec( $spec, $orientation['counterRotation'] );
+ }
+
+ $image->newImage( $spec['width'], $spec['height'], new ImagickPixel( $spec['fill'] ) );
+
+ foreach ( $spec['draws'] as $drawSpec ) {
+ $draw = new ImagickDraw();
+ $draw->setFillColor( $drawSpec['fill'] );
+ $draw->polygon( $drawSpec['shape'] );
+ $image->drawImage( $draw );
+ }
+
+ $image->setImageFormat( $format );
+
+ // this doesn't work, even though it's documented to do so...
+ // $image->setImageOrientation( $orientation['exifCode'] );
+
+ $image->writeImage( $filename );
+
+ // because the above setImageOrientation call doesn't work... nor can I get an external imagemagick binary to do this either...
+ // hacking this for now (only works if you have exiv2 installed, a program to read and manipulate exif)
+ if ( $wgExiv2Command ) {
+ $cmd = wfEscapeShellArg( $wgExiv2Command )
+ . " -M "
+ . wfEscapeShellArg( "set Exif.Image.Orientation " . $orientation['exifCode'] )
+ . " "
+ . wfEscapeShellArg( $filename );
+
+ $retval = 0;
+ $err = wfShellExec( $cmd, $retval );
+ if ( $retval !== 0 ) {
+ print "Error with $cmd: $retval, $err\n";
+ }
+ }
+
+
+ }
+
+ /**
+ * Given an image specification, produce rotated version
+ * This is used when simulating a rotated image capture with EXIF orientation
+ * @param $spec Object returned by getImageSpec
+ * @param $matrix 2x2 transformation matrix
+ * @return transformed Spec
+ */
+ private static function rotateImageSpec( &$spec, $matrix ) {
+ $tSpec = array();
+ $dims = self::matrixMultiply2x2( $matrix, $spec['width'], $spec['height'] );
+ $correctionX = 0;
+ $correctionY = 0;
+ if ( $dims['x'] < 0 ) {
+ $correctionX = abs( $dims['x'] );
+ }
+ if ( $dims['y'] < 0 ) {
+ $correctionY = abs( $dims['y'] );
+ }
+ $tSpec['width'] = abs( $dims['x'] );
+ $tSpec['height'] = abs( $dims['y'] );
+ $tSpec['fill'] = $spec['fill'];
+ $tSpec['draws'] = array();
+ foreach( $spec['draws'] as $draw ) {
+ $tDraw = array(
+ 'fill' => $draw['fill'],
+ 'shape' => array()
+ );
+ foreach( $draw['shape'] as $point ) {
+ $tPoint = self::matrixMultiply2x2( $matrix, $point['x'], $point['y'] );
+ $tPoint['x'] += $correctionX;
+ $tPoint['y'] += $correctionY;
+ $tDraw['shape'][] = $tPoint;
+ }
+ $tSpec['draws'][] = $tDraw;
+ }
+ return $tSpec;
+ }
+
+ /**
+ * Given a matrix and a pair of images, return new position
+ * @param $matrix: 2x2 rotation matrix
+ * @param $x: x-coordinate number
+ * @param $y: y-coordinate number
+ * @return Array transformed with properties x, y
+ */
+ private static function matrixMultiply2x2( $matrix, $x, $y ) {
+ return array(
+ 'x' => $x * $matrix[0][0] + $y * $matrix[0][1],
+ 'y' => $x * $matrix[1][0] + $y * $matrix[1][1]
+ );
+ }
+
+
+ /**
+ * Based on an image specification, write such an image to disk, using the command line ImageMagick program ('convert').
+ *
+ * Sample command line:
+ * $ convert -size 100x60 xc:rgb(90,87,45) \
+ * -draw 'fill rgb(12,34,56) polygon 41,39 44,57 50,57 41,39' \
+ * -draw 'fill rgb(99,123,231) circle 59,39 56,57' \
+ * -draw 'fill rgb(240,12,32) circle 50,21 50,3' filename.png
+ *
+ * @param $spec: spec describing background and shapes to draw
+ * @param $format: file format to write (unused by this method but kept so it has the same signature as writeImageWithApi)
+ * @param $filename: filename to write to
+ */
+ public function writeImageWithCommandLine( $spec, $format, $filename ) {
+ global $wgImageMagickConvertCommand;
+ $args = array();
+ $args[] = "-size " . wfEscapeShellArg( $spec['width'] . 'x' . $spec['height'] );
+ $args[] = wfEscapeShellArg( "xc:" . $spec['fill'] );
+ foreach( $spec['draws'] as $draw ) {
+ $fill = $draw['fill'];
+ $polygon = self::shapePointsToString( $draw['shape'] );
+ $drawCommand = "fill $fill polygon $polygon";
+ $args[] = '-draw ' . wfEscapeShellArg( $drawCommand );
+ }
+ $args[] = wfEscapeShellArg( $filename );
+
+ $command = wfEscapeShellArg( $wgImageMagickConvertCommand ) . " " . implode( " ", $args );
+ $retval = null;
+ wfShellExec( $command, $retval );
+ return ( $retval === 0 );
+ }
+
+ /**
+ * Generate a string of random colors for ImageMagick or SVG, like "rgb(12, 37, 98)"
+ *
+ * @return {String}
+ */
+ public function getRandomColor() {
+ $components = array();
+ for ($i = 0; $i <= 2; $i++ ) {
+ $components[] = mt_rand( 0, 255 );
+ }
+ return 'rgb(' . join(', ', $components) . ')';
+ }
+
+ /**
+ * Get an array of random pairs of random words, like array( array( 'foo', 'bar' ), array( 'quux', 'baz' ) );
+ *
+ * @param $number Integer: number of pairs
+ * @return Array: of two-element arrays
+ */
+ private function getRandomWordPairs( $number ) {
+ $lines = $this->getRandomLines( $number * 2 );
+ // construct pairs of words
+ $pairs = array();
+ $count = count( $lines );
+ for( $i = 0; $i < $count; $i += 2 ) {
+ $pairs[] = array( $lines[$i], $lines[$i+1] );
+ }
+ return $pairs;
+ }
+
+
+ /**
+ * Return N random lines from a file
+ *
+ * Will throw exception if the file could not be read or if it had fewer lines than requested.
+ *
+ * @param $number_desired Integer: number of lines desired
+ * @return Array: of exactly n elements, drawn randomly from lines the file
+ */
+ private function getRandomLines( $number_desired ) {
+ $filepath = $this->dictionaryFile;
+
+ // initialize array of lines
+ $lines = array();
+ for ( $i = 0; $i < $number_desired; $i++ ) {
+ $lines[] = null;
+ }
+
+ /*
+ * This algorithm obtains N random lines from a file in one single pass. It does this by replacing elements of
+ * a fixed-size array of lines, less and less frequently as it reads the file.
+ */
+ $fh = fopen( $filepath, "r" );
+ if ( !$fh ) {
+ throw new Exception( "couldn't open $filepath" );
+ }
+ $line_number = 0;
+ $max_index = $number_desired - 1;
+ while( !feof( $fh ) ) {
+ $line = fgets( $fh );
+ if ( $line !== false ) {
+ $line_number++;
+ $line = trim( $line );
+ if ( mt_rand( 0, $line_number ) <= $max_index ) {
+ $lines[ mt_rand( 0, $max_index ) ] = $line;
+ }
+ }
+ }
+ fclose( $fh );
+ if ( $line_number < $number_desired ) {
+ throw new Exception( "not enough lines in $filepath" );
+ }
+
+ return $lines;
+ }
+
+}
diff --git a/tests/phpunit/includes/api/format/ApiFormatPhpTest.php b/tests/phpunit/includes/api/format/ApiFormatPhpTest.php
new file mode 100644
index 00000000..8209f591
--- /dev/null
+++ b/tests/phpunit/includes/api/format/ApiFormatPhpTest.php
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * @group API
+ * @group Database
+ */
+class ApiFormatPhpTest extends ApiFormatTestBase {
+
+ function testValidPhpSyntax() {
+
+ $data = $this->apiRequest( 'php', array( 'action' => 'query', 'meta' => 'siteinfo' ) );
+
+ $this->assertInternalType( 'array', unserialize( $data ) );
+ $this->assertGreaterThan( 0, count( (array) $data ) );
+
+
+ }
+
+}
diff --git a/tests/phpunit/includes/api/format/ApiFormatTestBase.php b/tests/phpunit/includes/api/format/ApiFormatTestBase.php
new file mode 100644
index 00000000..a0b7b020
--- /dev/null
+++ b/tests/phpunit/includes/api/format/ApiFormatTestBase.php
@@ -0,0 +1,22 @@
+<?php
+
+abstract class ApiFormatTestBase extends ApiTestCase {
+ protected function apiRequest( $format, $params, $data = null ) {
+ $data = parent::doApiRequest( $params, $data, true );
+
+ $module = $data[3];
+
+ $printer = $module->createPrinterByName( $format );
+ $printer->setUnescapeAmps( false );
+
+ $printer->initPrinter( false );
+
+ ob_start();
+ $printer->execute();
+ $out = ob_get_clean();
+
+ $printer->closePrinter();
+
+ return $out;
+ }
+}
diff --git a/tests/phpunit/includes/api/generateRandomImages.php b/tests/phpunit/includes/api/generateRandomImages.php
new file mode 100644
index 00000000..f3a14e5b
--- /dev/null
+++ b/tests/phpunit/includes/api/generateRandomImages.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * Bootstrapping for test image file generation
+ *
+ * @file
+ */
+
+// Evaluate the include path relative to this file
+$IP = dirname( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) );
+
+// Start up MediaWiki in command-line mode
+require_once( "$IP/maintenance/Maintenance.php" );
+require("RandomImageGenerator.php");
+
+class GenerateRandomImages extends Maintenance {
+
+ public function execute() {
+
+ $getOptSpec = array(
+ 'dictionaryFile::',
+ 'minWidth::',
+ 'maxWidth::',
+ 'minHeight::',
+ 'maxHeight::',
+ 'shapesToDraw::',
+ 'shape::',
+
+ 'number::',
+ 'format::'
+ );
+ $options = getopt( null, $getOptSpec );
+
+ $format = isset( $options['format'] ) ? $options['format'] : 'jpg';
+ unset( $options['format'] );
+
+ $number = isset( $options['number'] ) ? intval( $options['number'] ) : 10;
+ unset( $options['number'] );
+
+ $randomImageGenerator = new RandomImageGenerator( $options );
+ $randomImageGenerator->writeImages( $number, $format );
+ }
+}
+
+$maintClass = 'GenerateRandomImages';
+require( RUN_MAINTENANCE_IF_MAIN );
+
+
diff --git a/tests/phpunit/includes/api/words.txt b/tests/phpunit/includes/api/words.txt
new file mode 100644
index 00000000..7ce23ee3
--- /dev/null
+++ b/tests/phpunit/includes/api/words.txt
@@ -0,0 +1,1000 @@
+Andaquian
+Anoplanthus
+Araquaju
+Astrophyton
+Avarish
+Batonga
+Bdellidae
+Betoyan
+Bismarck
+Britishness
+Carmen
+Chatillon
+Clement
+Coryphaena
+Croton
+Cyrillianism
+Dagomba
+Decimus
+Dichorisandra
+Duculinae
+Empusa
+Escallonia
+Fathometer
+Fon
+Fundulinae
+Gadswoons
+Gederathite
+Gemini
+Gerbera
+Gregarinida
+Gyracanthus
+Halopsychidae
+Hasidim
+Hemerobius
+Ichthyosauridae
+Iscariot
+Jeames
+Jesuitry
+Jovian
+Judaization
+Katie
+Ladin
+Langhian
+Lapithaean
+Lisette
+Macrochira
+Malaxis
+Malvastrum
+Maranhao
+Marxian
+Maurist
+Metrosideros
+Micky
+Microsporon
+Odacidae
+Ophiuchid
+Osmorhiza
+Paguma
+Palesman
+Papayaceae
+Pastinaca
+Philoxenian
+Pleurostigma
+Rarotongan
+Rhodoraceae
+Rong
+Saho
+Sanyakoan
+Sardanapalian
+Sauropoda
+Sedentaria
+Shambu
+Shukulumbwe
+Solonian
+Spaniardization
+Spirochaetaceae
+Stomatopoda
+Stratiotes
+Taiwanhemp
+Titanically
+Venetianed
+Victrola
+Yuman
+abatis
+abaton
+abjoint
+acanthoma
+acari
+acceptance
+actinography
+acuteness
+addiment
+adelite
+adelomorphic
+adelphogamy
+adipocele
+aelurophobia
+affined
+aflaunt
+agathokakological
+aischrolatreia
+alarmedly
+alebench
+aleurone
+allelotropic
+allerion
+alloplastic
+allowable
+alternacy
+alternariose
+altricial
+ambitionist
+amendment
+amiableness
+amicableness
+ammo
+amortizable
+anchorate
+anemometrically
+angelocracy
+angelological
+anodal
+anomalure
+antedate
+antiagglutinin
+antirationalist
+antiscorbutic
+antisplasher
+antithesize
+antiunionist
+antoecian
+apolegamic
+appropriation
+archididascalian
+archival
+arteriophlebotomy
+articulable
+asseveration
+assignation
+atelo
+atrienses
+atrophy
+atterminement
+atypic
+automower
+aveloz
+awrist
+azteca
+bairnteam
+balsamweed
+bannerman
+beardy
+becry
+beek
+beggarwise
+bescab
+bestness
+bethel
+bewildering
+bibliophilism
+bitterblain
+blakeberyed
+boccarella
+bocedization
+boobyalla
+bourbon
+bowbent
+bowerbird
+brachygnathous
+brail
+branchiferous
+brelaw
+brew
+brideweed
+bridgeable
+brombenzamide
+buddler
+burbankian
+burr
+buskin
+cacochymical
+calefactory
+caliper
+canaliculus
+candidature
+canellaceous
+canniness
+canning
+cantilene
+carbonatation
+carthamic
+caseum
+caudated
+causationist
+ceruleite
+chalder
+chalta
+charmel
+chekan
+chillness
+chirogymnast
+chirpling
+chlorinous
+cholanthrene
+chondroblast
+chromatography
+chromophilous
+chronical
+cicatrice
+cinchonine
+city
+clubbing
+coastal
+coaxially
+coercible
+coeternity
+coff
+coinventor
+collyba
+combinator
+complanation
+comprehensibility
+conchuela
+congenital
+context
+contranatural
+corallum
+cordately
+cornupete
+corolliferous
+coroneted
+corticosterone
+coseat
+cottage
+crocetin
+crossleted
+crottels
+curvedness
+cycadeous
+cyclism
+cylindrically
+cynanche
+cyrtoceratitic
+cystospasm
+danceress
+dancette
+dawny
+daydreamy
+debar
+decarburization
+decorousness
+decrepitness
+delirious
+deozonizer
+dermatosis
+desma
+deutencephalic
+diacetate
+diarthrodial
+diathermy
+dicolic
+dimastigate
+dimidiation
+dipetto
+disavowable
+disintrench
+disman
+dismay
+disorder
+disoxygenation
+dithionous
+dogman
+dragonfly
+dramatical
+drawspan
+drubbly
+drunk
+duskly
+ecderonic
+ectocuniform
+ectocyst
+ehrwaldite
+electrocute
+elemicin
+embracing
+emotionality
+enactment
+enamor
+enclave
+endameba
+endochylous
+endocrinologist
+endolymph
+endothecal
+entasia
+epigeous
+episcopicide
+epitrichial
+erminee
+erraticalness
+eruptivity
+erythrocytoschisis
+esperance
+estuous
+eucrystalline
+eugeny
+evacuant
+everbloomer
+evocation
+exarchateship
+exasperate
+excorticate
+excrementary
+exile
+expandedly
+exponency
+expressionist
+expulsion
+extemporary
+extollation
+extortive
+extrabulbar
+extraprostatic
+facticide
+fairer
+fakery
+fasibitikite
+fatiscent
+fearless
+febrifuge
+ferie
+fibrousness
+fingered
+fisheye
+flagpole
+flagrantness
+fleche
+fluidism
+folliculin
+footbreadth
+forceps
+forecontrive
+forthbring
+foveated
+fuchsin
+fungicidal
+funori
+gamelang
+gametically
+garvanzo
+gasoliner
+gastrophile
+germproof
+gerontism
+gigantical
+glaciology
+godmotherhood
+gooseherd
+gordunite
+gove
+gracilis
+greathead
+grieveship
+guidable
+gyromancy
+gyrostat
+habitus
+hailweed
+handhole
+hangalai
+haznadar
+heliced
+hemihypertrophy
+hemimorphic
+hemistrumectomy
+heptavalent
+heptite
+herbalist
+herpetology
+hesperid
+hexacarbon
+hieromnemon
+hobbyless
+holodactylic
+homoeoarchy
+hopperings
+hospitable
+houseboat
+huh
+huntedly
+hydroponics
+hydrosomal
+hyperdactylia
+hyperperistalsis
+hypogeocarpous
+ideogram
+idiopathical
+illegitimate
+imambarah
+impotently
+improvise
+impuberal
+inaccurately
+incarnant
+inchoation
+incliner
+incredulous
+indiscriminateness
+indulgenced
+inebriation
+inexpressiveness
+infibulate
+inflectedness
+iniome
+ink
+inquietly
+insaturable
+insinuative
+instiller
+institutive
+insultproof
+interactionist
+intercensal
+interpenetrable
+intertranspicuous
+intrinsicality
+inwards
+iridiocyte
+iridoparalysis
+irreportable
+isoprene
+isosmotic
+izard
+jacuaru
+jaculative
+jerkined
+joe
+joyous
+julienne
+justicehood
+kali
+kalidium
+katha
+kathal
+keelage
+keratomycosis
+khaki
+khedival
+kinkily
+knife
+kolo
+kraken
+kwarta
+labba
+labber
+laboress
+lacunar
+latch
+lauric
+lawter
+lectotype
+leeches
+legible
+lepidosteoid
+leucobasalt
+leverer
+libellate
+limnimeter
+lithography
+lithotypic
+locomotor
+logarithmetically
+logistician
+lyncine
+lysogenesis
+machan
+macromyelon
+maharana
+mandibulate
+manganapatite
+marchpane
+mas
+masochistic
+mastaba
+matching
+meditatively
+megalopolitan
+melaniline
+mentum
+mercaptides
+mestome
+metasomatism
+meterless
+micronuclear
+micropetalous
+microreaction
+microsporophore
+mileway
+milliarium
+millisecond
+misbind
+miscollocation
+misreader
+modernicide
+modification
+modulant
+monkfish
+monoamino
+monocarbide
+monographical
+morphinomaniac
+mullein
+munge
+mutilate
+mycophagist
+myelosarcoma
+myospasm
+myriadly
+nagaika
+naphthionate
+natant
+naviculaeform
+nayward
+neallotype
+necrophilia
+nectared
+neigher
+neogamous
+neurodynia
+neurorthopteran
+nidation
+nieceship
+nitrobacteria
+nitrosification
+nogheaded
+nonassertive
+noneuphonious
+nonextant
+nonincrease
+nonintermittent
+nonmetallic
+nonprehensile
+nonremunerative
+nonsocial
+nonvesting
+noontime
+noreaster
+nounal
+nub
+nucleoplasm
+nullisome
+numero
+numerous
+oblongatal
+observe
+obtusilingual
+obvert
+occipitoatlantal
+oceanside
+ochlophobist
+odontiasis
+opalescence
+opticon
+oraculousness
+orarium
+organically
+orthopedically
+ostosis
+overadvance
+overbuilt
+overdiscouragement
+overdoer
+overhardy
+overjocular
+overmagnify
+overofficered
+overpotent
+overprizer
+overrunner
+overshrink
+oversimply
+oversplash
+ovology
+oxskin
+oxychloride
+oxygenant
+ozokerite
+pactional
+palaeoanthropography
+palaeographical
+palaeopsychology
+palliasse
+palpebral
+pandaric
+pantelegraph
+papicolist
+papulate
+parakinetic
+parasitism
+parochialic
+parochialize
+passionlike
+patch
+paucidentate
+pawnbrokeress
+pecite
+pecky
+pedipulation
+pellitory
+perfilograph
+periblast
+perigemmal
+periost
+periplus
+perishable
+periwig
+permansive
+persistingly
+persymmetrical
+phantom
+phasmatrope
+philocaly
+philogyny
+philosophister
+philotherianism
+phorology
+phototrophic
+phrator
+phratral
+phthisipneumony
+physogastry
+phytologic
+phytoptid
+pianograph
+picqueter
+piculet
+pigeoner
+pimaric
+pinesap
+pist
+planometer
+platano
+playful
+plea
+pleuropneumonic
+plowwoman
+plump
+pluviographical
+pneumocele
+podophthalmate
+polyad
+polythalamian
+poppyhead
+portamento
+portmanteau
+portraitlike
+possible
+potassamide
+powderer
+praepubis
+preanesthetic
+prebarbaric
+predealer
+predomination
+prefactory
+preirrigational
+prelector
+presbytership
+presecure
+preservable
+prespecialist
+preventionism
+prewound
+princely
+priorship
+proannexationist
+proanthropos
+probeable
+probouleutic
+profitless
+proplasma
+prosectorial
+protecting
+protochemistry
+protosulphate
+pseudoataxia
+psilology
+psychoneurotic
+pterygial
+publicist
+purgation
+purplishness
+putatively
+pyracene
+pyrenomycete
+pyromancy
+pyrophone
+quadroon
+quailhead
+qualifier
+quaternal
+rabblelike
+rambunctious
+rapidness
+ratably
+rationalism
+razor
+reannoy
+recultivation
+regulable
+reimplant
+reimposition
+reimprison
+reinjure
+reinspiration
+reintroduce
+remantle
+reprehensibility
+reptant
+require
+resteal
+restful
+returnability
+revisableness
+rewash
+rewhirl
+reyield
+rhizotomy
+rhodamine
+rigwiddie
+rimester
+ripper
+rippet
+rockish
+rockwards
+rollicky
+roosters
+rooted
+rosal
+rozum
+saccharated
+sagamore
+sagy
+salesmanship
+salivous
+sallet
+salta
+saprostomous
+satiation
+sauropsid
+sawarra
+sawback
+scabish
+scabrate
+scampavia
+scientificophilosophical
+scirrosity
+scoliometer
+scolopendrelloid
+secantly
+seignioral
+semibull
+semic
+seminarianism
+semiped
+semiprivate
+semispherical
+semispontaneous
+seneschal
+septendecimal
+serotherapist
+servation
+sesquisulphuret
+severish
+sextipartite
+sextubercular
+shipyard
+shuckpen
+siderosis
+silex
+sillyhow
+silverbelly
+silverbelly
+simulacrum
+sisham
+sixte
+skeiner
+skiapod
+slopped
+slubby
+smalts
+sockmaker
+solute
+somethingness
+somnify
+southwester
+spathilla
+spectrochemical
+sphagnology
+spinales
+spiriting
+spirling
+spirochetemia
+spreadboard
+spurflower
+squawdom
+squeezing
+staircase
+staker
+stamphead
+statolith
+stekan
+stellulate
+stinker
+stomodaea
+streamingly
+strikingness
+strouthocamelian
+stuprum
+subacutely
+subboreal
+subcontractor
+subendorsement
+subprofitable
+subserviate
+subsneer
+subungual
+sucuruju
+sugan
+sulphocarbolate
+summerwood
+superficialist
+superinference
+superregenerative
+supplicate
+suspendible
+synchronizer
+syntectic
+tachyglossate
+tailless
+taintment
+takingly
+taletelling
+tarpon
+tasteful
+taxeater
+taxy
+teache
+teachless
+teg
+tegmen
+teletyper
+temperable
+ten
+tenent
+teskere
+testes
+thallogen
+thapsia
+thewness
+thickety
+thiobacteria
+thorniness
+throwing
+thyroprivic
+tinnitus
+tocalote
+tolerationist
+tonalamatl
+torvous
+totality
+tottering
+toug
+tracheopathia
+tragedical
+translucent
+trifoveolate
+trilaurin
+trophoplasmatic
+trunkless
+turbanless
+turnpiker
+twangle
+twitterboned
+ultraornate
+umbilication
+unabatingly
+unabjured
+unadequateness
+unaffectedness
+unarriving
+unassorted
+unattacked
+unbenumbed
+unboasted
+unburning
+uncensorious
+uncongested
+uncontemnedly
+uncontemporary
+uncrook
+uncrystallizability
+uncurb
+uncustomariness
+underbillow
+undercanopy
+underestimation
+underhanging
+underpetticoated
+underpropped
+undersole
+understocking
+underworld
+undevout
+undisappointing
+undistinctive
+unfiscal
+unfluted
+unfreckled
+ungentilize
+unglobe
+unhelped
+unhomogeneously
+unifoliate
+uninflammable
+uninterrogated
+unisonal
+unkindled
+unlikeableness
+unlisty
+unlocked
+unmoving
+unmultipliable
+unnestled
+unnoticed
+unobservable
+unobviated
+unoffensively
+unofficerlike
+unpoetic
+unpractically
+unquestionableness
+unrehearsed
+unrevised
+unrhetorical
+unsadden
+unsaluting
+unscriptural
+unseeking
+unshowed
+unsolicitous
+unsprouted
+unsubjective
+unsubsidized
+unsymbolic
+untenant
+unterrified
+untranquil
+untraversed
+untrusty
+untying
+unwillful
+unwinding
+upspring
+uptwist
+urachovesical
+uropygial
+vagabondism
+varicoid
+varletess
+vasal
+ventrocaudal
+verisimilitude
+vermigerous
+vibrometer
+viminal
+virus
+vocationalism
+voguey
+vulnerability
+waggle
+wamblingly
+warmus
+waxer
+waying
+wedgeable
+wellmaker
+whomever
+wigged
+witchlike
+wokas
+woodrowel
+woodsman
+woolding
+xanthelasmic
+xiphosternum
+yachtman
+yachtsmanlike
+yelp
+zoophytal \ No newline at end of file
diff --git a/tests/phpunit/includes/db/DatabaseSqliteTest.php b/tests/phpunit/includes/db/DatabaseSqliteTest.php
new file mode 100644
index 00000000..914ab27c
--- /dev/null
+++ b/tests/phpunit/includes/db/DatabaseSqliteTest.php
@@ -0,0 +1,312 @@
+<?php
+
+class MockDatabaseSqlite extends DatabaseSqliteStandalone {
+ var $lastQuery;
+
+ function __construct( ) {
+ parent::__construct( ':memory:' );
+ }
+
+ function query( $sql, $fname = '', $tempIgnore = false ) {
+ $this->lastQuery = $sql;
+ return true;
+ }
+
+ function replaceVars( $s ) {
+ return parent::replaceVars( $s );
+ }
+}
+
+/**
+ * @group sqlite
+ */
+class DatabaseSqliteTest extends MediaWikiTestCase {
+ var $db;
+
+ public function setUp() {
+ if ( !Sqlite::isPresent() ) {
+ $this->markTestSkipped( 'No SQLite support detected' );
+ }
+ $this->db = new MockDatabaseSqlite();
+ if ( version_compare( $this->db->getServerVersion(), '3.6.0', '<' ) ) {
+ $this->markTestSkipped( "SQLite at least 3.6 required, {$this->db->getServerVersion()} found" );
+ }
+ }
+
+ private function replaceVars( $sql ) {
+ // normalize spacing to hide implementation details
+ return preg_replace( '/\s+/', ' ', $this->db->replaceVars( $sql ) );
+ }
+
+ private function assertResultIs( $expected, $res ) {
+ $this->assertNotNull( $res );
+ $i = 0;
+ foreach( $res as $row ) {
+ foreach( $expected[$i] as $key => $value ) {
+ $this->assertTrue( isset( $row->$key ) );
+ $this->assertEquals( $value, $row->$key );
+ }
+ $i++;
+ }
+ $this->assertEquals( count( $expected ), $i, 'Unexpected number of rows' );
+ }
+
+ public function testReplaceVars() {
+ $this->assertEquals( 'foo', $this->replaceVars( 'foo' ), "Don't break anything accidentally" );
+
+ $this->assertEquals( "CREATE TABLE /**/foo (foo_key INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "
+ . "foo_bar TEXT, foo_name TEXT NOT NULL DEFAULT '', foo_int INTEGER, foo_int2 INTEGER );",
+ $this->replaceVars( "CREATE TABLE /**/foo (foo_key int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ foo_bar char(13), foo_name varchar(255) binary NOT NULL DEFAULT '', foo_int tinyint ( 8 ), foo_int2 int(16) ) ENGINE=MyISAM;" )
+ );
+
+ $this->assertEquals( "CREATE TABLE foo ( foo1 REAL, foo2 REAL, foo3 REAL );",
+ $this->replaceVars( "CREATE TABLE foo ( foo1 FLOAT, foo2 DOUBLE( 1,10), foo3 DOUBLE PRECISION );" )
+ );
+
+ $this->assertEquals( "CREATE TABLE foo ( foo_binary1 BLOB, foo_binary2 BLOB );",
+ $this->replaceVars( "CREATE TABLE foo ( foo_binary1 binary(16), foo_binary2 varbinary(32) );" )
+ );
+
+ $this->assertEquals( "CREATE TABLE text ( text_foo TEXT );",
+ $this->replaceVars( "CREATE TABLE text ( text_foo tinytext );" ),
+ 'Table name changed'
+ );
+
+ $this->assertEquals( "CREATE TABLE foo ( foobar INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL );",
+ $this->replaceVars("CREATE TABLE foo ( foobar INT PRIMARY KEY NOT NULL AUTO_INCREMENT );" )
+ );
+ $this->assertEquals( "CREATE TABLE foo ( foobar INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL );",
+ $this->replaceVars("CREATE TABLE foo ( foobar INT PRIMARY KEY AUTO_INCREMENT NOT NULL );" )
+ );
+
+ $this->assertEquals( "CREATE TABLE enums( enum1 TEXT, myenum TEXT)",
+ $this->replaceVars( "CREATE TABLE enums( enum1 ENUM('A', 'B'), myenum ENUM ('X', 'Y'))" )
+ );
+
+ $this->assertEquals( "ALTER TABLE foo ADD COLUMN foo_bar INTEGER DEFAULT 42",
+ $this->replaceVars( "ALTER TABLE foo\nADD COLUMN foo_bar int(10) unsigned DEFAULT 42" )
+ );
+ }
+
+ public function testTableName() {
+ // @todo Moar!
+ $db = new DatabaseSqliteStandalone( ':memory:' );
+ $this->assertEquals( 'foo', $db->tableName( 'foo' ) );
+ $this->assertEquals( 'sqlite_master', $db->tableName( 'sqlite_master' ) );
+ $db->tablePrefix( 'foo' );
+ $this->assertEquals( 'sqlite_master', $db->tableName( 'sqlite_master' ) );
+ $this->assertEquals( 'foobar', $db->tableName( 'bar' ) );
+ }
+
+ public function testDuplicateTableStructure() {
+ $db = new DatabaseSqliteStandalone( ':memory:' );
+ $db->query( 'CREATE TABLE foo(foo, barfoo)' );
+
+ $db->duplicateTableStructure( 'foo', 'bar' );
+ $this->assertEquals( 'CREATE TABLE "bar"(foo, barfoo)',
+ $db->selectField( 'sqlite_master', 'sql', array( 'name' => 'bar' ) ),
+ 'Normal table duplication'
+ );
+
+ $db->duplicateTableStructure( 'foo', 'baz', true );
+ $this->assertEquals( 'CREATE TABLE "baz"(foo, barfoo)',
+ $db->selectField( 'sqlite_temp_master', 'sql', array( 'name' => 'baz' ) ),
+ 'Creation of temporary duplicate'
+ );
+ $this->assertEquals( 0,
+ $db->selectField( 'sqlite_master', 'COUNT(*)', array( 'name' => 'baz' ) ),
+ 'Create a temporary duplicate only'
+ );
+ }
+
+ public function testDuplicateTableStructureVirtual() {
+ $db = new DatabaseSqliteStandalone( ':memory:' );
+ if ( $db->getFulltextSearchModule() != 'FTS3' ) {
+ $this->markTestSkipped( 'FTS3 not supported, cannot create virtual tables' );
+ }
+ $db->query( 'CREATE VIRTUAL TABLE "foo" USING FTS3(foobar)' );
+
+ $db->duplicateTableStructure( 'foo', 'bar' );
+ $this->assertEquals( 'CREATE VIRTUAL TABLE "bar" USING FTS3(foobar)',
+ $db->selectField( 'sqlite_master', 'sql', array( 'name' => 'bar' ) ),
+ 'Duplication of virtual tables'
+ );
+
+ $db->duplicateTableStructure( 'foo', 'baz', true );
+ $this->assertEquals( 'CREATE VIRTUAL TABLE "baz" USING FTS3(foobar)',
+ $db->selectField( 'sqlite_master', 'sql', array( 'name' => 'baz' ) ),
+ "Can't create temporary virtual tables, should fall back to non-temporary duplication"
+ );
+ }
+
+ public function testDeleteJoin() {
+ $db = new DatabaseSqliteStandalone( ':memory:' );
+ $db->query( 'CREATE TABLE a (a_1)', __METHOD__ );
+ $db->query( 'CREATE TABLE b (b_1, b_2)', __METHOD__ );
+ $db->insert( 'a', array(
+ array( 'a_1' => 1 ),
+ array( 'a_1' => 2 ),
+ array( 'a_1' => 3 ),
+ ),
+ __METHOD__
+ );
+ $db->insert( 'b', array(
+ array( 'b_1' => 2, 'b_2' => 'a' ),
+ array( 'b_1' => 3, 'b_2' => 'b' ),
+ ),
+ __METHOD__
+ );
+ $db->deleteJoin( 'a', 'b', 'a_1', 'b_1', array( 'b_2' => 'a' ), __METHOD__ );
+ $res = $db->query( "SELECT * FROM a", __METHOD__ );
+ $this->assertResultIs( array(
+ array( 'a_1' => 1 ),
+ array( 'a_1' => 3 ),
+ ),
+ $res
+ );
+ }
+
+ public function testEntireSchema() {
+ global $IP;
+
+ $result = Sqlite::checkSqlSyntax( "$IP/maintenance/tables.sql" );
+ if ( $result !== true ) {
+ $this->fail( $result );
+ }
+ $this->assertTrue( true ); // avoid test being marked as incomplete due to lack of assertions
+ }
+
+ /**
+ * Runs upgrades of older databases and compares results with current schema
+ * @todo: currently only checks list of tables
+ */
+ public function testUpgrades() {
+ global $IP, $wgVersion;
+
+ // Versions tested
+ $versions = array(
+ //'1.13', disabled for now, was totally screwed up
+ // SQLite wasn't included in 1.14
+ '1.15',
+ '1.16',
+ '1.17',
+ );
+
+ // Mismatches for these columns we can safely ignore
+ $ignoredColumns = array(
+ 'user_newtalk.user_last_timestamp', // r84185
+ );
+
+ $currentDB = new DatabaseSqliteStandalone( ':memory:' );
+ $currentDB->sourceFile( "$IP/maintenance/tables.sql" );
+ $currentTables = $this->getTables( $currentDB );
+ sort( $currentTables );
+
+ foreach ( $versions as $version ) {
+ $versions = "upgrading from $version to $wgVersion";
+ $db = $this->prepareDB( $version );
+ $tables = $this->getTables( $db );
+ $this->assertEquals( $currentTables, $tables, "Different tables $versions" );
+ foreach ( $tables as $table ) {
+ $currentCols = $this->getColumns( $currentDB, $table );
+ $cols = $this->getColumns( $db, $table );
+ $this->assertEquals(
+ array_keys( $currentCols ),
+ array_keys( $cols ),
+ "Mismatching columns for table \"$table\" $versions"
+ );
+ foreach ( $currentCols as $name => $column ) {
+ $fullName = "$table.$name";
+ $this->assertEquals(
+ (bool)$column->pk,
+ (bool)$cols[$name]->pk,
+ "PRIMARY KEY status does not match for column $fullName $versions"
+ );
+ if ( !in_array( $fullName, $ignoredColumns ) ) {
+ $this->assertEquals(
+ (bool)$column->notnull,
+ (bool)$cols[$name]->notnull,
+ "NOT NULL status does not match for column $fullName $versions"
+ );
+ $this->assertEquals(
+ $column->dflt_value,
+ $cols[$name]->dflt_value,
+ "Default values does not match for column $fullName $versions"
+ );
+ }
+ }
+ $currentIndexes = $this->getIndexes( $currentDB, $table );
+ $indexes = $this->getIndexes( $db, $table );
+ $this->assertEquals(
+ array_keys( $currentIndexes ),
+ array_keys( $indexes ),
+ "mismatching indexes for table \"$table\" $versions"
+ );
+ }
+ $db->close();
+ }
+ }
+
+ private function prepareDB( $version ) {
+ static $maint = null;
+ if ( $maint === null ) {
+ $maint = new FakeMaintenance();
+ $maint->loadParamsAndArgs( null, array( 'quiet' => 1 ) );
+ }
+
+ $db = new DatabaseSqliteStandalone( ':memory:' );
+ $db->sourceFile( dirname( __FILE__ ) . "/sqlite/tables-$version.sql" );
+ $updater = DatabaseUpdater::newForDB( $db, false, $maint );
+ $updater->doUpdates( array( 'core' ) );
+ return $db;
+ }
+
+ private function getTables( $db ) {
+ $list = array_flip( $db->listTables() );
+ $excluded = array(
+ 'math', // moved out of core in 1.18
+ 'searchindex',
+ 'searchindex_content',
+ 'searchindex_segments',
+ 'searchindex_segdir',
+ // FTS4 ready!!1
+ 'searchindex_docsize',
+ 'searchindex_stat',
+ );
+ foreach ( $excluded as $t ) {
+ unset( $list[$t] );
+ }
+ $list = array_flip( $list );
+ sort( $list );
+ return $list;
+ }
+
+ private function getColumns( $db, $table ) {
+ $cols = array();
+ $res = $db->query( "PRAGMA table_info($table)" );
+ $this->assertNotNull( $res );
+ foreach ( $res as $col ) {
+ $cols[$col->name] = $col;
+ }
+ ksort( $cols );
+ return $cols;
+ }
+
+ private function getIndexes( $db, $table ) {
+ $indexes = array();
+ $res = $db->query( "PRAGMA index_list($table)" );
+ $this->assertNotNull( $res );
+ foreach ( $res as $index ) {
+ $res2 = $db->query( "PRAGMA index_info({$index->name})" );
+ $this->assertNotNull( $res2 );
+ $index->columns = array();
+ foreach ( $res2 as $col ) {
+ $index->columns[] = $col;
+ }
+ $indexes[$index->name] = $index;
+ }
+ ksort( $indexes );
+ return $indexes;
+ }
+}
diff --git a/tests/phpunit/includes/db/DatabaseTest.php b/tests/phpunit/includes/db/DatabaseTest.php
new file mode 100644
index 00000000..d480ac6e
--- /dev/null
+++ b/tests/phpunit/includes/db/DatabaseTest.php
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * @group Database
+ */
+class DatabaseTest extends MediaWikiTestCase {
+ var $db;
+
+ function setUp() {
+ $this->db = wfGetDB( DB_SLAVE );
+ }
+
+ function testAddQuotesNull() {
+ $check = "NULL";
+ if ( $this->db->getType() === 'sqlite' || $this->db->getType() === 'oracle' ) {
+ $check = "''";
+ }
+ $this->assertEquals( $check, $this->db->addQuotes( null ) );
+ }
+
+ function testAddQuotesInt() {
+ # returning just "1234" should be ok too, though...
+ # maybe
+ $this->assertEquals(
+ "'1234'",
+ $this->db->addQuotes( 1234 ) );
+ }
+
+ function testAddQuotesFloat() {
+ # returning just "1234.5678" would be ok too, though
+ $this->assertEquals(
+ "'1234.5678'",
+ $this->db->addQuotes( 1234.5678 ) );
+ }
+
+ function testAddQuotesString() {
+ $this->assertEquals(
+ "'string'",
+ $this->db->addQuotes( 'string' ) );
+ }
+
+ function testAddQuotesStringQuote() {
+ $check = "'string''s cause trouble'";
+ if ( $this->db->getType() === 'mysql' ) {
+ $check = "'string\'s cause trouble'";
+ }
+ $this->assertEquals(
+ $check,
+ $this->db->addQuotes( "string's cause trouble" ) );
+ }
+
+ function testFillPreparedEmpty() {
+ $sql = $this->db->fillPrepared(
+ 'SELECT * FROM interwiki', array() );
+ $this->assertEquals(
+ "SELECT * FROM interwiki",
+ $sql );
+ }
+
+ function testFillPreparedQuestion() {
+ $sql = $this->db->fillPrepared(
+ 'SELECT * FROM cur WHERE cur_namespace=? AND cur_title=?',
+ array( 4, "Snicker's_paradox" ) );
+
+ $check = "SELECT * FROM cur WHERE cur_namespace='4' AND cur_title='Snicker''s_paradox'";
+ if ( $this->db->getType() === 'mysql' ) {
+ $check = "SELECT * FROM cur WHERE cur_namespace='4' AND cur_title='Snicker\'s_paradox'";
+ }
+ $this->assertEquals( $check, $sql );
+ }
+
+ function testFillPreparedBang() {
+ $sql = $this->db->fillPrepared(
+ 'SELECT user_id FROM ! WHERE user_name=?',
+ array( '"user"', "Slash's Dot" ) );
+
+ $check = "SELECT user_id FROM \"user\" WHERE user_name='Slash''s Dot'";
+ if ( $this->db->getType() === 'mysql' ) {
+ $check = "SELECT user_id FROM \"user\" WHERE user_name='Slash\'s Dot'";
+ }
+ $this->assertEquals( $check, $sql );
+ }
+
+ function testFillPreparedRaw() {
+ $sql = $this->db->fillPrepared(
+ "SELECT * FROM cur WHERE cur_title='This_\\&_that,_WTF\\?\\!'",
+ array( '"user"', "Slash's Dot" ) );
+ $this->assertEquals(
+ "SELECT * FROM cur WHERE cur_title='This_&_that,_WTF?!'",
+ $sql );
+ }
+
+}
+
+
diff --git a/tests/phpunit/includes/db/sqlite/tables-1.13.sql b/tests/phpunit/includes/db/sqlite/tables-1.13.sql
new file mode 100644
index 00000000..a0dcb553
--- /dev/null
+++ b/tests/phpunit/includes/db/sqlite/tables-1.13.sql
@@ -0,0 +1,342 @@
+-- This is a copy of SQLite schema from MediaWiki 1.13 used for updater testing
+
+CREATE TABLE /*$wgDBprefix*/user (
+ user_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_name varchar(255) default '',
+ user_real_name varchar(255) default '',
+ user_password tinyblob ,
+ user_newpassword tinyblob ,
+ user_newpass_time BLOB,
+ user_email tinytext ,
+ user_options blob ,
+ user_touched BLOB default '',
+ user_token BLOB default '',
+ user_email_authenticated BLOB,
+ user_email_token BLOB,
+ user_email_token_expires BLOB,
+ user_registration BLOB,
+ user_editcount int) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/user_groups (
+ ug_user INTEGER default '0',
+ ug_group varBLOB default '') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/user_newtalk (
+ user_id INTEGER default '0',
+ user_ip varBLOB default '',
+ user_last_timestamp BLOB default '') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/page (
+ page_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ page_namespace INTEGER ,
+ page_title varchar(255) ,
+ page_restrictions tinyblob ,
+ page_counter bigint default '0',
+ page_is_redirect tinyint default '0',
+ page_is_new tinyint default '0',
+ page_random real ,
+ page_touched BLOB default '',
+ page_latest INTEGER ,
+ page_len INTEGER ) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/revision (
+ rev_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ rev_page INTEGER ,
+ rev_text_id INTEGER ,
+ rev_comment tinyblob ,
+ rev_user INTEGER default '0',
+ rev_user_text varchar(255) default '',
+ rev_timestamp BLOB default '',
+ rev_minor_edit tinyint default '0',
+ rev_deleted tinyint default '0',
+ rev_len int,
+ rev_parent_id INTEGER default NULL) /*$wgDBTableOptions*/ ;
+
+CREATE TABLE /*$wgDBprefix*/text (
+ old_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ old_text mediumblob ,
+ old_flags tinyblob ) /*$wgDBTableOptions*/ ;
+
+CREATE TABLE /*$wgDBprefix*/archive (
+ ar_namespace INTEGER default '0',
+ ar_title varchar(255) default '',
+ ar_text mediumblob ,
+ ar_comment tinyblob ,
+ ar_user INTEGER default '0',
+ ar_user_text varchar(255) ,
+ ar_timestamp BLOB default '',
+ ar_minor_edit tinyint default '0',
+ ar_flags tinyblob ,
+ ar_rev_id int,
+ ar_text_id int,
+ ar_deleted tinyint default '0',
+ ar_len int,
+ ar_page_id int,
+ ar_parent_id INTEGER default NULL) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/pagelinks (
+ pl_from INTEGER default '0',
+ pl_namespace INTEGER default '0',
+ pl_title varchar(255) default '') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/templatelinks (
+ tl_from INTEGER default '0',
+ tl_namespace INTEGER default '0',
+ tl_title varchar(255) default '') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/imagelinks (
+ il_from INTEGER default '0',
+ il_to varchar(255) default '') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/categorylinks (
+ cl_from INTEGER default '0',
+ cl_to varchar(255) default '',
+ cl_sortkey varchar(70) default '',
+ cl_timestamp timestamp ) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/category (
+ cat_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ cat_title varchar(255) ,
+ cat_pages INTEGER signed default 0,
+ cat_subcats INTEGER signed default 0,
+ cat_files INTEGER signed default 0,
+ cat_hidden tinyint default 0) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/externallinks (
+ el_from INTEGER default '0',
+ el_to blob ,
+ el_index blob ) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/langlinks (
+ ll_from INTEGER default '0',
+ ll_lang varBLOB default '',
+ ll_title varchar(255) default '') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/site_stats (
+ ss_row_id INTEGER ,
+ ss_total_views bigint default '0',
+ ss_total_edits bigint default '0',
+ ss_good_articles bigint default '0',
+ ss_total_pages bigint default '-1',
+ ss_users bigint default '-1',
+ ss_admins INTEGER default '-1',
+ ss_images INTEGER default '0') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/hitcounter (
+ hc_id INTEGER
+) ;
+
+CREATE TABLE /*$wgDBprefix*/ipblocks (
+ ipb_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ ipb_address tinyblob ,
+ ipb_user INTEGER default '0',
+ ipb_by INTEGER default '0',
+ ipb_by_text varchar(255) default '',
+ ipb_reason tinyblob ,
+ ipb_timestamp BLOB default '',
+ ipb_auto bool default 0,
+ ipb_anon_only bool default 0,
+ ipb_create_account bool default 1,
+ ipb_enable_autoblock bool default '1',
+ ipb_expiry varBLOB default '',
+ ipb_range_start tinyblob ,
+ ipb_range_end tinyblob ,
+ ipb_deleted bool default 0,
+ ipb_block_email bool default 0) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/image (
+ img_name varchar(255) default '',
+ img_size INTEGER default '0',
+ img_width INTEGER default '0',
+ img_height INTEGER default '0',
+ img_metadata mediumblob ,
+ img_bits INTEGER default '0',
+ img_media_type TEXT default NULL,
+ img_major_mime TEXT default "unknown",
+ img_minor_mime varBLOB default "unknown",
+ img_description tinyblob ,
+ img_user INTEGER default '0',
+ img_user_text varchar(255) ,
+ img_timestamp varBLOB default '',
+ img_sha1 varBLOB default '') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/oldimage (
+ oi_name varchar(255) default '',
+ oi_archive_name varchar(255) default '',
+ oi_size INTEGER default 0,
+ oi_width INTEGER default 0,
+ oi_height INTEGER default 0,
+ oi_bits INTEGER default 0,
+ oi_description tinyblob ,
+ oi_user INTEGER default '0',
+ oi_user_text varchar(255) ,
+ oi_timestamp BLOB default '',
+ oi_metadata mediumblob ,
+ oi_media_type TEXT default NULL,
+ oi_major_mime TEXT default "unknown",
+ oi_minor_mime varBLOB default "unknown",
+ oi_deleted tinyint default '0',
+ oi_sha1 varBLOB default '') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/filearchive (
+ fa_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ fa_name varchar(255) default '',
+ fa_archive_name varchar(255) default '',
+ fa_storage_group varBLOB,
+ fa_storage_key varBLOB default '',
+ fa_deleted_user int,
+ fa_deleted_timestamp BLOB default '',
+ fa_deleted_reason text,
+ fa_size INTEGER default '0',
+ fa_width INTEGER default '0',
+ fa_height INTEGER default '0',
+ fa_metadata mediumblob,
+ fa_bits INTEGER default '0',
+ fa_media_type TEXT default NULL,
+ fa_major_mime TEXT default "unknown",
+ fa_minor_mime varBLOB default "unknown",
+ fa_description tinyblob,
+ fa_user INTEGER default '0',
+ fa_user_text varchar(255) ,
+ fa_timestamp BLOB default '',
+ fa_deleted tinyint default '0') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/recentchanges (
+ rc_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ rc_timestamp varBLOB default '',
+ rc_cur_time varBLOB default '',
+ rc_user INTEGER default '0',
+ rc_user_text varchar(255) ,
+ rc_namespace INTEGER default '0',
+ rc_title varchar(255) default '',
+ rc_comment varchar(255) default '',
+ rc_minor tinyint default '0',
+ rc_bot tinyint default '0',
+ rc_new tinyint default '0',
+ rc_cur_id INTEGER default '0',
+ rc_this_oldid INTEGER default '0',
+ rc_last_oldid INTEGER default '0',
+ rc_type tinyint default '0',
+ rc_moved_to_ns tinyint default '0',
+ rc_moved_to_title varchar(255) default '',
+ rc_patrolled tinyint default '0',
+ rc_ip varBLOB default '',
+ rc_old_len int,
+ rc_new_len int,
+ rc_deleted tinyint default '0',
+ rc_logid INTEGER default '0',
+ rc_log_type varBLOB NULL default NULL,
+ rc_log_action varBLOB NULL default NULL,
+ rc_params blob NULL) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/watchlist (
+ wl_user INTEGER ,
+ wl_namespace INTEGER default '0',
+ wl_title varchar(255) default '',
+ wl_notificationtimestamp varBLOB) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/math (
+ math_inputhash varBLOB ,
+ math_outputhash varBLOB ,
+ math_html_conservativeness tinyint ,
+ math_html text,
+ math_mathml text) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/searchindex (
+ si_page INTEGER ,
+ si_title varchar(255) default '',
+ si_text mediumtext ) ;
+
+CREATE TABLE /*$wgDBprefix*/interwiki (
+ iw_prefix varchar(32) ,
+ iw_url blob ,
+ iw_local bool ,
+ iw_trans tinyint default 0) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/querycache (
+ qc_type varBLOB ,
+ qc_value INTEGER default '0',
+ qc_namespace INTEGER default '0',
+ qc_title varchar(255) default '') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/objectcache (
+ keyname varBLOB default '',
+ value mediumblob,
+ exptime datetime) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/transcache (
+ tc_url varBLOB ,
+ tc_contents text,
+ tc_time INTEGER ) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/logging (
+ log_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ log_type varBLOB default '',
+ log_action varBLOB default '',
+ log_timestamp BLOB default '19700101000000',
+ log_user INTEGER default 0,
+ log_namespace INTEGER default 0,
+ log_title varchar(255) default '',
+ log_comment varchar(255) default '',
+ log_params blob ,
+ log_deleted tinyint default '0') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/trackbacks (
+ tb_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ tb_page INTEGER REFERENCES /*$wgDBprefix*/page(page_id) ON DELETE CASCADE,
+ tb_title varchar(255) ,
+ tb_url blob ,
+ tb_ex text,
+ tb_name varchar(255)) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/job (
+ job_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ job_cmd varBLOB default '',
+ job_namespace INTEGER ,
+ job_title varchar(255) ,
+ job_params blob ) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/querycache_info (
+ qci_type varBLOB default '',
+ qci_timestamp BLOB default '19700101000000') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/redirect (
+ rd_from INTEGER default '0',
+ rd_namespace INTEGER default '0',
+ rd_title varchar(255) default '') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/querycachetwo (
+ qcc_type varBLOB ,
+ qcc_value INTEGER default '0',
+ qcc_namespace INTEGER default '0',
+ qcc_title varchar(255) default '',
+ qcc_namespacetwo INTEGER default '0',
+ qcc_titletwo varchar(255) default '') /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/page_restrictions (
+ pr_page INTEGER ,
+ pr_type varBLOB ,
+ pr_level varBLOB ,
+ pr_cascade tinyint ,
+ pr_user INTEGER NULL,
+ pr_expiry varBLOB NULL,
+ pr_id INTEGER PRIMARY KEY AUTOINCREMENT) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/protected_titles (
+ pt_namespace INTEGER ,
+ pt_title varchar(255) ,
+ pt_user INTEGER ,
+ pt_reason tinyblob,
+ pt_timestamp BLOB ,
+ pt_expiry varBLOB default '',
+ pt_create_perm varBLOB ) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/page_props (
+ pp_page INTEGER ,
+ pp_propname varBLOB ,
+ pp_value blob ) /*$wgDBTableOptions*/;
+
+CREATE TABLE /*$wgDBprefix*/updatelog (
+ ul_key varchar(255) ) /*$wgDBTableOptions*/;
+
+
diff --git a/tests/phpunit/includes/db/sqlite/tables-1.15.sql b/tests/phpunit/includes/db/sqlite/tables-1.15.sql
new file mode 100644
index 00000000..901bac52
--- /dev/null
+++ b/tests/phpunit/includes/db/sqlite/tables-1.15.sql
@@ -0,0 +1,454 @@
+-- This is a copy of MediaWiki 1.15 schema shared by MySQL and SQLite.
+-- It is used for updater testing. Comments are stripped to decrease
+-- file size, as we don't need to maintain it.
+
+CREATE TABLE /*_*/user (
+ user_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ user_name varchar(255) binary NOT NULL default '',
+ user_real_name varchar(255) binary NOT NULL default '',
+ user_password tinyblob NOT NULL,
+ user_newpassword tinyblob NOT NULL,
+ user_newpass_time binary(14),
+ user_email tinytext NOT NULL,
+ user_options blob NOT NULL,
+ user_touched binary(14) NOT NULL default '',
+ user_token binary(32) NOT NULL default '',
+ user_email_authenticated binary(14),
+ user_email_token binary(32),
+ user_email_token_expires binary(14),
+ user_registration binary(14),
+ user_editcount int
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/user_name ON /*_*/user (user_name);
+CREATE INDEX /*i*/user_email_token ON /*_*/user (user_email_token);
+CREATE TABLE /*_*/user_groups (
+ ug_user int unsigned NOT NULL default 0,
+ ug_group varbinary(16) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ug_user_group ON /*_*/user_groups (ug_user,ug_group);
+CREATE INDEX /*i*/ug_group ON /*_*/user_groups (ug_group);
+CREATE TABLE /*_*/user_newtalk (
+ user_id int NOT NULL default 0,
+ user_ip varbinary(40) NOT NULL default '',
+ user_last_timestamp binary(14) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/un_user_id ON /*_*/user_newtalk (user_id);
+CREATE INDEX /*i*/un_user_ip ON /*_*/user_newtalk (user_ip);
+CREATE TABLE /*_*/page (
+ page_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ page_namespace int NOT NULL,
+ page_title varchar(255) binary NOT NULL,
+ page_restrictions tinyblob NOT NULL,
+ page_counter bigint unsigned NOT NULL default 0,
+ page_is_redirect tinyint unsigned NOT NULL default 0,
+ page_is_new tinyint unsigned NOT NULL default 0,
+ page_random real unsigned NOT NULL,
+ page_touched binary(14) NOT NULL default '',
+ page_latest int unsigned NOT NULL,
+ page_len int unsigned NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/name_title ON /*_*/page (page_namespace,page_title);
+CREATE INDEX /*i*/page_random ON /*_*/page (page_random);
+CREATE INDEX /*i*/page_len ON /*_*/page (page_len);
+CREATE TABLE /*_*/revision (
+ rev_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ rev_page int unsigned NOT NULL,
+ rev_text_id int unsigned NOT NULL,
+ rev_comment tinyblob NOT NULL,
+ rev_user int unsigned NOT NULL default 0,
+ rev_user_text varchar(255) binary NOT NULL default '',
+ rev_timestamp binary(14) NOT NULL default '',
+ rev_minor_edit tinyint unsigned NOT NULL default 0,
+ rev_deleted tinyint unsigned NOT NULL default 0,
+ rev_len int unsigned,
+ rev_parent_id int unsigned default NULL
+) /*$wgDBTableOptions*/ MAX_ROWS=10000000 AVG_ROW_LENGTH=1024;
+CREATE UNIQUE INDEX /*i*/rev_page_id ON /*_*/revision (rev_page, rev_id);
+CREATE INDEX /*i*/rev_timestamp ON /*_*/revision (rev_timestamp);
+CREATE INDEX /*i*/page_timestamp ON /*_*/revision (rev_page,rev_timestamp);
+CREATE INDEX /*i*/user_timestamp ON /*_*/revision (rev_user,rev_timestamp);
+CREATE INDEX /*i*/usertext_timestamp ON /*_*/revision (rev_user_text,rev_timestamp);
+CREATE TABLE /*_*/text (
+ old_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ old_text mediumblob NOT NULL,
+ old_flags tinyblob NOT NULL
+) /*$wgDBTableOptions*/ MAX_ROWS=10000000 AVG_ROW_LENGTH=10240;
+CREATE TABLE /*_*/archive (
+ ar_namespace int NOT NULL default 0,
+ ar_title varchar(255) binary NOT NULL default '',
+ ar_text mediumblob NOT NULL,
+ ar_comment tinyblob NOT NULL,
+ ar_user int unsigned NOT NULL default 0,
+ ar_user_text varchar(255) binary NOT NULL,
+ ar_timestamp binary(14) NOT NULL default '',
+ ar_minor_edit tinyint NOT NULL default 0,
+ ar_flags tinyblob NOT NULL,
+ ar_rev_id int unsigned,
+ ar_text_id int unsigned,
+ ar_deleted tinyint unsigned NOT NULL default 0,
+ ar_len int unsigned,
+ ar_page_id int unsigned,
+ ar_parent_id int unsigned default NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX /*i*/ar_usertext_timestamp ON /*_*/archive (ar_user_text,ar_timestamp);
+CREATE TABLE /*_*/pagelinks (
+ pl_from int unsigned NOT NULL default 0,
+ pl_namespace int NOT NULL default 0,
+ pl_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/pl_from ON /*_*/pagelinks (pl_from,pl_namespace,pl_title);
+CREATE UNIQUE INDEX /*i*/pl_namespace ON /*_*/pagelinks (pl_namespace,pl_title,pl_from);
+CREATE TABLE /*_*/templatelinks (
+ tl_from int unsigned NOT NULL default 0,
+ tl_namespace int NOT NULL default 0,
+ tl_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/tl_from ON /*_*/templatelinks (tl_from,tl_namespace,tl_title);
+CREATE UNIQUE INDEX /*i*/tl_namespace ON /*_*/templatelinks (tl_namespace,tl_title,tl_from);
+CREATE TABLE /*_*/imagelinks (
+ il_from int unsigned NOT NULL default 0,
+ il_to varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/il_from ON /*_*/imagelinks (il_from,il_to);
+CREATE UNIQUE INDEX /*i*/il_to ON /*_*/imagelinks (il_to,il_from);
+CREATE TABLE /*_*/categorylinks (
+ cl_from int unsigned NOT NULL default 0,
+ cl_to varchar(255) binary NOT NULL default '',
+ cl_sortkey varchar(70) binary NOT NULL default '',
+ cl_timestamp timestamp NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/cl_from ON /*_*/categorylinks (cl_from,cl_to);
+CREATE INDEX /*i*/cl_sortkey ON /*_*/categorylinks (cl_to,cl_sortkey,cl_from);
+CREATE INDEX /*i*/cl_timestamp ON /*_*/categorylinks (cl_to,cl_timestamp);
+CREATE TABLE /*_*/category (
+ cat_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ cat_title varchar(255) binary NOT NULL,
+ cat_pages int signed NOT NULL default 0,
+ cat_subcats int signed NOT NULL default 0,
+ cat_files int signed NOT NULL default 0,
+ cat_hidden tinyint unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/cat_title ON /*_*/category (cat_title);
+CREATE INDEX /*i*/cat_pages ON /*_*/category (cat_pages);
+CREATE TABLE /*_*/externallinks (
+ el_from int unsigned NOT NULL default 0,
+ el_to blob NOT NULL,
+ el_index blob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/el_from ON /*_*/externallinks (el_from, el_to(40));
+CREATE INDEX /*i*/el_to ON /*_*/externallinks (el_to(60), el_from);
+CREATE INDEX /*i*/el_index ON /*_*/externallinks (el_index(60));
+CREATE TABLE /*_*/langlinks (
+ ll_from int unsigned NOT NULL default 0,
+
+ ll_lang varbinary(20) NOT NULL default '',
+ ll_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ll_from ON /*_*/langlinks (ll_from, ll_lang);
+CREATE INDEX /*i*/ll_lang ON /*_*/langlinks (ll_lang, ll_title);
+CREATE TABLE /*_*/site_stats (
+ ss_row_id int unsigned NOT NULL,
+ ss_total_views bigint unsigned default 0,
+ ss_total_edits bigint unsigned default 0,
+ ss_good_articles bigint unsigned default 0,
+ ss_total_pages bigint default '-1',
+ ss_users bigint default '-1',
+ ss_active_users bigint default '-1',
+ ss_admins int default '-1',
+ ss_images int default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ss_row_id ON /*_*/site_stats (ss_row_id);
+CREATE TABLE /*_*/hitcounter (
+ hc_id int unsigned NOT NULL
+) ENGINE=HEAP MAX_ROWS=25000;
+CREATE TABLE /*_*/ipblocks (
+ ipb_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ ipb_address tinyblob NOT NULL,
+ ipb_user int unsigned NOT NULL default 0,
+ ipb_by int unsigned NOT NULL default 0,
+ ipb_by_text varchar(255) binary NOT NULL default '',
+ ipb_reason tinyblob NOT NULL,
+ ipb_timestamp binary(14) NOT NULL default '',
+ ipb_auto bool NOT NULL default 0,
+ ipb_anon_only bool NOT NULL default 0,
+ ipb_create_account bool NOT NULL default 1,
+ ipb_enable_autoblock bool NOT NULL default '1',
+ ipb_expiry varbinary(14) NOT NULL default '',
+ ipb_range_start tinyblob NOT NULL,
+ ipb_range_end tinyblob NOT NULL,
+ ipb_deleted bool NOT NULL default 0,
+ ipb_block_email bool NOT NULL default 0,
+ ipb_allow_usertalk bool NOT NULL default 0
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/ipb_address ON /*_*/ipblocks (ipb_address(255), ipb_user, ipb_auto, ipb_anon_only);
+CREATE INDEX /*i*/ipb_user ON /*_*/ipblocks (ipb_user);
+CREATE INDEX /*i*/ipb_range ON /*_*/ipblocks (ipb_range_start(8), ipb_range_end(8));
+CREATE INDEX /*i*/ipb_timestamp ON /*_*/ipblocks (ipb_timestamp);
+CREATE INDEX /*i*/ipb_expiry ON /*_*/ipblocks (ipb_expiry);
+CREATE TABLE /*_*/image (
+ img_name varchar(255) binary NOT NULL default '' PRIMARY KEY,
+ img_size int unsigned NOT NULL default 0,
+ img_width int NOT NULL default 0,
+ img_height int NOT NULL default 0,
+ img_metadata mediumblob NOT NULL,
+ img_bits int NOT NULL default 0,
+ img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+ img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") NOT NULL default "unknown",
+ img_minor_mime varbinary(32) NOT NULL default "unknown",
+ img_description tinyblob NOT NULL,
+ img_user int unsigned NOT NULL default 0,
+ img_user_text varchar(255) binary NOT NULL,
+ img_timestamp varbinary(14) NOT NULL default '',
+ img_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/img_usertext_timestamp ON /*_*/image (img_user_text,img_timestamp);
+CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
+CREATE INDEX /*i*/img_timestamp ON /*_*/image (img_timestamp);
+CREATE INDEX /*i*/img_sha1 ON /*_*/image (img_sha1);
+CREATE TABLE /*_*/oldimage (
+ oi_name varchar(255) binary NOT NULL default '',
+ oi_archive_name varchar(255) binary NOT NULL default '',
+ oi_size int unsigned NOT NULL default 0,
+ oi_width int NOT NULL default 0,
+ oi_height int NOT NULL default 0,
+ oi_bits int NOT NULL default 0,
+ oi_description tinyblob NOT NULL,
+ oi_user int unsigned NOT NULL default 0,
+ oi_user_text varchar(255) binary NOT NULL,
+ oi_timestamp binary(14) NOT NULL default '',
+ oi_metadata mediumblob NOT NULL,
+ oi_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+ oi_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") NOT NULL default "unknown",
+ oi_minor_mime varbinary(32) NOT NULL default "unknown",
+ oi_deleted tinyint unsigned NOT NULL default 0,
+ oi_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
+CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
+CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name,oi_archive_name(14));
+CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1);
+CREATE TABLE /*_*/filearchive (
+ fa_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ fa_name varchar(255) binary NOT NULL default '',
+ fa_archive_name varchar(255) binary default '',
+ fa_storage_group varbinary(16),
+ fa_storage_key varbinary(64) default '',
+ fa_deleted_user int,
+ fa_deleted_timestamp binary(14) default '',
+ fa_deleted_reason text,
+ fa_size int unsigned default 0,
+ fa_width int default 0,
+ fa_height int default 0,
+ fa_metadata mediumblob,
+ fa_bits int default 0,
+ fa_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+ fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") default "unknown",
+ fa_minor_mime varbinary(32) default "unknown",
+ fa_description tinyblob,
+ fa_user int unsigned default 0,
+ fa_user_text varchar(255) binary,
+ fa_timestamp binary(14) default '',
+ fa_deleted tinyint unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/fa_name ON /*_*/filearchive (fa_name, fa_timestamp);
+CREATE INDEX /*i*/fa_storage_group ON /*_*/filearchive (fa_storage_group, fa_storage_key);
+CREATE INDEX /*i*/fa_deleted_timestamp ON /*_*/filearchive (fa_deleted_timestamp);
+CREATE INDEX /*i*/fa_user_timestamp ON /*_*/filearchive (fa_user_text,fa_timestamp);
+CREATE TABLE /*_*/recentchanges (
+ rc_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ rc_timestamp varbinary(14) NOT NULL default '',
+ rc_cur_time varbinary(14) NOT NULL default '',
+ rc_user int unsigned NOT NULL default 0,
+ rc_user_text varchar(255) binary NOT NULL,
+ rc_namespace int NOT NULL default 0,
+ rc_title varchar(255) binary NOT NULL default '',
+ rc_comment varchar(255) binary NOT NULL default '',
+ rc_minor tinyint unsigned NOT NULL default 0,
+ rc_bot tinyint unsigned NOT NULL default 0,
+ rc_new tinyint unsigned NOT NULL default 0,
+ rc_cur_id int unsigned NOT NULL default 0,
+ rc_this_oldid int unsigned NOT NULL default 0,
+ rc_last_oldid int unsigned NOT NULL default 0,
+ rc_type tinyint unsigned NOT NULL default 0,
+ rc_moved_to_ns tinyint unsigned NOT NULL default 0,
+ rc_moved_to_title varchar(255) binary NOT NULL default '',
+ rc_patrolled tinyint unsigned NOT NULL default 0,
+ rc_ip varbinary(40) NOT NULL default '',
+ rc_old_len int,
+ rc_new_len int,
+ rc_deleted tinyint unsigned NOT NULL default 0,
+ rc_logid int unsigned NOT NULL default 0,
+ rc_log_type varbinary(255) NULL default NULL,
+ rc_log_action varbinary(255) NULL default NULL,
+ rc_params blob NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/rc_timestamp ON /*_*/recentchanges (rc_timestamp);
+CREATE INDEX /*i*/rc_namespace_title ON /*_*/recentchanges (rc_namespace, rc_title);
+CREATE INDEX /*i*/rc_cur_id ON /*_*/recentchanges (rc_cur_id);
+CREATE INDEX /*i*/new_name_timestamp ON /*_*/recentchanges (rc_new,rc_namespace,rc_timestamp);
+CREATE INDEX /*i*/rc_ip ON /*_*/recentchanges (rc_ip);
+CREATE INDEX /*i*/rc_ns_usertext ON /*_*/recentchanges (rc_namespace, rc_user_text);
+CREATE INDEX /*i*/rc_user_text ON /*_*/recentchanges (rc_user_text, rc_timestamp);
+CREATE TABLE /*_*/watchlist (
+ wl_user int unsigned NOT NULL,
+ wl_namespace int NOT NULL default 0,
+ wl_title varchar(255) binary NOT NULL default '',
+ wl_notificationtimestamp varbinary(14)
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/wl_user ON /*_*/watchlist (wl_user, wl_namespace, wl_title);
+CREATE INDEX /*i*/namespace_title ON /*_*/watchlist (wl_namespace, wl_title);
+CREATE TABLE /*_*/math (
+ math_inputhash varbinary(16) NOT NULL,
+ math_outputhash varbinary(16) NOT NULL,
+ math_html_conservativeness tinyint NOT NULL,
+ math_html text,
+ math_mathml text
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/math_inputhash ON /*_*/math (math_inputhash);
+CREATE TABLE /*_*/searchindex (
+ si_page int unsigned NOT NULL,
+ si_title varchar(255) NOT NULL default '',
+ si_text mediumtext NOT NULL
+) ENGINE=MyISAM;
+CREATE UNIQUE INDEX /*i*/si_page ON /*_*/searchindex (si_page);
+CREATE FULLTEXT INDEX /*i*/si_title ON /*_*/searchindex (si_title);
+CREATE FULLTEXT INDEX /*i*/si_text ON /*_*/searchindex (si_text);
+CREATE TABLE /*_*/interwiki (
+ iw_prefix varchar(32) NOT NULL,
+ iw_url blob NOT NULL,
+ iw_local bool NOT NULL,
+ iw_trans tinyint NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/iw_prefix ON /*_*/interwiki (iw_prefix);
+CREATE TABLE /*_*/querycache (
+ qc_type varbinary(32) NOT NULL,
+ qc_value int unsigned NOT NULL default 0,
+ qc_namespace int NOT NULL default 0,
+ qc_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/qc_type ON /*_*/querycache (qc_type,qc_value);
+CREATE TABLE /*_*/objectcache (
+ keyname varbinary(255) NOT NULL default '' PRIMARY KEY,
+ value mediumblob,
+ exptime datetime
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/exptime ON /*_*/objectcache (exptime);
+CREATE TABLE /*_*/transcache (
+ tc_url varbinary(255) NOT NULL,
+ tc_contents text,
+ tc_time int NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/tc_url_idx ON /*_*/transcache (tc_url);
+CREATE TABLE /*_*/logging (
+ log_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ log_type varbinary(10) NOT NULL default '',
+ log_action varbinary(10) NOT NULL default '',
+ log_timestamp binary(14) NOT NULL default '19700101000000',
+ log_user int unsigned NOT NULL default 0,
+ log_namespace int NOT NULL default 0,
+ log_title varchar(255) binary NOT NULL default '',
+ log_comment varchar(255) NOT NULL default '',
+ log_params blob NOT NULL,
+ log_deleted tinyint unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/type_time ON /*_*/logging (log_type, log_timestamp);
+CREATE INDEX /*i*/user_time ON /*_*/logging (log_user, log_timestamp);
+CREATE INDEX /*i*/page_time ON /*_*/logging (log_namespace, log_title, log_timestamp);
+CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp);
+CREATE TABLE /*_*/trackbacks (
+ tb_id int PRIMARY KEY AUTO_INCREMENT,
+ tb_page int REFERENCES /*_*/page(page_id) ON DELETE CASCADE,
+ tb_title varchar(255) NOT NULL,
+ tb_url blob NOT NULL,
+ tb_ex text,
+ tb_name varchar(255)
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/tb_page ON /*_*/trackbacks (tb_page);
+CREATE TABLE /*_*/job (
+ job_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ job_cmd varbinary(60) NOT NULL default '',
+ job_namespace int NOT NULL,
+ job_title varchar(255) binary NOT NULL,
+ job_params blob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/job_cmd ON /*_*/job (job_cmd, job_namespace, job_title);
+CREATE TABLE /*_*/querycache_info (
+ qci_type varbinary(32) NOT NULL default '',
+ qci_timestamp binary(14) NOT NULL default '19700101000000'
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/qci_type ON /*_*/querycache_info (qci_type);
+CREATE TABLE /*_*/redirect (
+ rd_from int unsigned NOT NULL default 0 PRIMARY KEY,
+ rd_namespace int NOT NULL default 0,
+ rd_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/rd_ns_title ON /*_*/redirect (rd_namespace,rd_title,rd_from);
+CREATE TABLE /*_*/querycachetwo (
+ qcc_type varbinary(32) NOT NULL,
+ qcc_value int unsigned NOT NULL default 0,
+ qcc_namespace int NOT NULL default 0,
+ qcc_title varchar(255) binary NOT NULL default '',
+ qcc_namespacetwo int NOT NULL default 0,
+ qcc_titletwo varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/qcc_type ON /*_*/querycachetwo (qcc_type,qcc_value);
+CREATE INDEX /*i*/qcc_title ON /*_*/querycachetwo (qcc_type,qcc_namespace,qcc_title);
+CREATE INDEX /*i*/qcc_titletwo ON /*_*/querycachetwo (qcc_type,qcc_namespacetwo,qcc_titletwo);
+CREATE TABLE /*_*/page_restrictions (
+ pr_page int NOT NULL,
+ pr_type varbinary(60) NOT NULL,
+ pr_level varbinary(60) NOT NULL,
+ pr_cascade tinyint NOT NULL,
+ pr_user int NULL,
+ pr_expiry varbinary(14) NULL,
+ pr_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/pr_pagetype ON /*_*/page_restrictions (pr_page,pr_type);
+CREATE INDEX /*i*/pr_typelevel ON /*_*/page_restrictions (pr_type,pr_level);
+CREATE INDEX /*i*/pr_level ON /*_*/page_restrictions (pr_level);
+CREATE INDEX /*i*/pr_cascade ON /*_*/page_restrictions (pr_cascade);
+CREATE TABLE /*_*/protected_titles (
+ pt_namespace int NOT NULL,
+ pt_title varchar(255) binary NOT NULL,
+ pt_user int unsigned NOT NULL,
+ pt_reason tinyblob,
+ pt_timestamp binary(14) NOT NULL,
+ pt_expiry varbinary(14) NOT NULL default '',
+ pt_create_perm varbinary(60) NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/pt_namespace_title ON /*_*/protected_titles (pt_namespace,pt_title);
+CREATE INDEX /*i*/pt_timestamp ON /*_*/protected_titles (pt_timestamp);
+CREATE TABLE /*_*/page_props (
+ pp_page int NOT NULL,
+ pp_propname varbinary(60) NOT NULL,
+ pp_value blob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/pp_page_propname ON /*_*/page_props (pp_page,pp_propname);
+CREATE TABLE /*_*/updatelog (
+ ul_key varchar(255) NOT NULL PRIMARY KEY
+) /*$wgDBTableOptions*/;
+CREATE TABLE /*_*/change_tag (
+ ct_rc_id int NULL,
+ ct_log_id int NULL,
+ ct_rev_id int NULL,
+ ct_tag varchar(255) NOT NULL,
+ ct_params blob NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/change_tag_rc_tag ON /*_*/change_tag (ct_rc_id,ct_tag);
+CREATE UNIQUE INDEX /*i*/change_tag_log_tag ON /*_*/change_tag (ct_log_id,ct_tag);
+CREATE UNIQUE INDEX /*i*/change_tag_rev_tag ON /*_*/change_tag (ct_rev_id,ct_tag);
+CREATE INDEX /*i*/change_tag_tag_id ON /*_*/change_tag (ct_tag,ct_rc_id,ct_rev_id,ct_log_id);
+CREATE TABLE /*_*/tag_summary (
+ ts_rc_id int NULL,
+ ts_log_id int NULL,
+ ts_rev_id int NULL,
+ ts_tags blob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/tag_summary_rc_id ON /*_*/tag_summary (ts_rc_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_log_id ON /*_*/tag_summary (ts_log_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_rev_id ON /*_*/tag_summary (ts_rev_id);
+CREATE TABLE /*_*/valid_tag (
+ vt_tag varchar(255) NOT NULL PRIMARY KEY
+) /*$wgDBTableOptions*/;
diff --git a/tests/phpunit/includes/db/sqlite/tables-1.16.sql b/tests/phpunit/includes/db/sqlite/tables-1.16.sql
new file mode 100644
index 00000000..6e56add2
--- /dev/null
+++ b/tests/phpunit/includes/db/sqlite/tables-1.16.sql
@@ -0,0 +1,483 @@
+-- This is a copy of MediaWiki 1.16 schema shared by MySQL and SQLite.
+-- It is used for updater testing. Comments are stripped to decrease
+-- file size, as we don't need to maintain it.
+
+CREATE TABLE /*_*/user (
+ user_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ user_name varchar(255) binary NOT NULL default '',
+ user_real_name varchar(255) binary NOT NULL default '',
+ user_password tinyblob NOT NULL,
+ user_newpassword tinyblob NOT NULL,
+ user_newpass_time binary(14),
+ user_email tinytext NOT NULL,
+ user_options blob NOT NULL,
+ user_touched binary(14) NOT NULL default '',
+ user_token binary(32) NOT NULL default '',
+ user_email_authenticated binary(14),
+ user_email_token binary(32),
+ user_email_token_expires binary(14),
+ user_registration binary(14),
+ user_editcount int
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/user_name ON /*_*/user (user_name);
+CREATE INDEX /*i*/user_email_token ON /*_*/user (user_email_token);
+CREATE TABLE /*_*/user_groups (
+ ug_user int unsigned NOT NULL default 0,
+ ug_group varbinary(16) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ug_user_group ON /*_*/user_groups (ug_user,ug_group);
+CREATE INDEX /*i*/ug_group ON /*_*/user_groups (ug_group);
+CREATE TABLE /*_*/user_newtalk (
+ user_id int NOT NULL default 0,
+ user_ip varbinary(40) NOT NULL default '',
+ user_last_timestamp binary(14) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/un_user_id ON /*_*/user_newtalk (user_id);
+CREATE INDEX /*i*/un_user_ip ON /*_*/user_newtalk (user_ip);
+CREATE TABLE /*_*/user_properties (
+ up_user int NOT NULL,
+ up_property varbinary(32) NOT NULL,
+ up_value blob
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/user_properties_user_property ON /*_*/user_properties (up_user,up_property);
+CREATE INDEX /*i*/user_properties_property ON /*_*/user_properties (up_property);
+CREATE TABLE /*_*/page (
+ page_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ page_namespace int NOT NULL,
+ page_title varchar(255) binary NOT NULL,
+ page_restrictions tinyblob NOT NULL,
+ page_counter bigint unsigned NOT NULL default 0,
+ page_is_redirect tinyint unsigned NOT NULL default 0,
+ page_is_new tinyint unsigned NOT NULL default 0,
+ page_random real unsigned NOT NULL,
+ page_touched binary(14) NOT NULL default '',
+ page_latest int unsigned NOT NULL,
+ page_len int unsigned NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/name_title ON /*_*/page (page_namespace,page_title);
+CREATE INDEX /*i*/page_random ON /*_*/page (page_random);
+CREATE INDEX /*i*/page_len ON /*_*/page (page_len);
+CREATE TABLE /*_*/revision (
+ rev_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ rev_page int unsigned NOT NULL,
+ rev_text_id int unsigned NOT NULL,
+ rev_comment tinyblob NOT NULL,
+ rev_user int unsigned NOT NULL default 0,
+ rev_user_text varchar(255) binary NOT NULL default '',
+ rev_timestamp binary(14) NOT NULL default '',
+ rev_minor_edit tinyint unsigned NOT NULL default 0,
+ rev_deleted tinyint unsigned NOT NULL default 0,
+ rev_len int unsigned,
+ rev_parent_id int unsigned default NULL
+) /*$wgDBTableOptions*/ MAX_ROWS=10000000 AVG_ROW_LENGTH=1024;
+CREATE UNIQUE INDEX /*i*/rev_page_id ON /*_*/revision (rev_page, rev_id);
+CREATE INDEX /*i*/rev_timestamp ON /*_*/revision (rev_timestamp);
+CREATE INDEX /*i*/page_timestamp ON /*_*/revision (rev_page,rev_timestamp);
+CREATE INDEX /*i*/user_timestamp ON /*_*/revision (rev_user,rev_timestamp);
+CREATE INDEX /*i*/usertext_timestamp ON /*_*/revision (rev_user_text,rev_timestamp);
+CREATE TABLE /*_*/text (
+ old_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ old_text mediumblob NOT NULL,
+ old_flags tinyblob NOT NULL
+) /*$wgDBTableOptions*/ MAX_ROWS=10000000 AVG_ROW_LENGTH=10240;
+CREATE TABLE /*_*/archive (
+ ar_namespace int NOT NULL default 0,
+ ar_title varchar(255) binary NOT NULL default '',
+ ar_text mediumblob NOT NULL,
+ ar_comment tinyblob NOT NULL,
+ ar_user int unsigned NOT NULL default 0,
+ ar_user_text varchar(255) binary NOT NULL,
+ ar_timestamp binary(14) NOT NULL default '',
+ ar_minor_edit tinyint NOT NULL default 0,
+ ar_flags tinyblob NOT NULL,
+ ar_rev_id int unsigned,
+ ar_text_id int unsigned,
+ ar_deleted tinyint unsigned NOT NULL default 0,
+ ar_len int unsigned,
+ ar_page_id int unsigned,
+ ar_parent_id int unsigned default NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX /*i*/ar_usertext_timestamp ON /*_*/archive (ar_user_text,ar_timestamp);
+CREATE TABLE /*_*/pagelinks (
+ pl_from int unsigned NOT NULL default 0,
+ pl_namespace int NOT NULL default 0,
+ pl_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/pl_from ON /*_*/pagelinks (pl_from,pl_namespace,pl_title);
+CREATE UNIQUE INDEX /*i*/pl_namespace ON /*_*/pagelinks (pl_namespace,pl_title,pl_from);
+CREATE TABLE /*_*/templatelinks (
+ tl_from int unsigned NOT NULL default 0,
+ tl_namespace int NOT NULL default 0,
+ tl_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/tl_from ON /*_*/templatelinks (tl_from,tl_namespace,tl_title);
+CREATE UNIQUE INDEX /*i*/tl_namespace ON /*_*/templatelinks (tl_namespace,tl_title,tl_from);
+CREATE TABLE /*_*/imagelinks (
+ il_from int unsigned NOT NULL default 0,
+ il_to varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/il_from ON /*_*/imagelinks (il_from,il_to);
+CREATE UNIQUE INDEX /*i*/il_to ON /*_*/imagelinks (il_to,il_from);
+CREATE TABLE /*_*/categorylinks (
+ cl_from int unsigned NOT NULL default 0,
+ cl_to varchar(255) binary NOT NULL default '',
+ cl_sortkey varchar(70) binary NOT NULL default '',
+ cl_timestamp timestamp NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/cl_from ON /*_*/categorylinks (cl_from,cl_to);
+CREATE INDEX /*i*/cl_sortkey ON /*_*/categorylinks (cl_to,cl_sortkey,cl_from);
+CREATE INDEX /*i*/cl_timestamp ON /*_*/categorylinks (cl_to,cl_timestamp);
+CREATE TABLE /*_*/category (
+ cat_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ cat_title varchar(255) binary NOT NULL,
+ cat_pages int signed NOT NULL default 0,
+ cat_subcats int signed NOT NULL default 0,
+ cat_files int signed NOT NULL default 0,
+ cat_hidden tinyint unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/cat_title ON /*_*/category (cat_title);
+CREATE INDEX /*i*/cat_pages ON /*_*/category (cat_pages);
+CREATE TABLE /*_*/externallinks (
+ el_from int unsigned NOT NULL default 0,
+ el_to blob NOT NULL,
+ el_index blob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/el_from ON /*_*/externallinks (el_from, el_to(40));
+CREATE INDEX /*i*/el_to ON /*_*/externallinks (el_to(60), el_from);
+CREATE INDEX /*i*/el_index ON /*_*/externallinks (el_index(60));
+CREATE TABLE /*_*/external_user (
+ eu_local_id int unsigned NOT NULL PRIMARY KEY,
+ eu_external_id varchar(255) binary NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/eu_external_id ON /*_*/external_user (eu_external_id);
+CREATE TABLE /*_*/langlinks (
+ ll_from int unsigned NOT NULL default 0,
+ ll_lang varbinary(20) NOT NULL default '',
+ ll_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ll_from ON /*_*/langlinks (ll_from, ll_lang);
+CREATE INDEX /*i*/ll_lang ON /*_*/langlinks (ll_lang, ll_title);
+CREATE TABLE /*_*/site_stats (
+ ss_row_id int unsigned NOT NULL,
+ ss_total_views bigint unsigned default 0,
+ ss_total_edits bigint unsigned default 0,
+ ss_good_articles bigint unsigned default 0,
+ ss_total_pages bigint default '-1',
+ ss_users bigint default '-1',
+ ss_active_users bigint default '-1',
+ ss_admins int default '-1',
+ ss_images int default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ss_row_id ON /*_*/site_stats (ss_row_id);
+CREATE TABLE /*_*/hitcounter (
+ hc_id int unsigned NOT NULL
+) ENGINE=HEAP MAX_ROWS=25000;
+CREATE TABLE /*_*/ipblocks (
+ ipb_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ ipb_address tinyblob NOT NULL,
+ ipb_user int unsigned NOT NULL default 0,
+ ipb_by int unsigned NOT NULL default 0,
+ ipb_by_text varchar(255) binary NOT NULL default '',
+ ipb_reason tinyblob NOT NULL,
+ ipb_timestamp binary(14) NOT NULL default '',
+ ipb_auto bool NOT NULL default 0,
+ ipb_anon_only bool NOT NULL default 0,
+ ipb_create_account bool NOT NULL default 1,
+ ipb_enable_autoblock bool NOT NULL default '1',
+ ipb_expiry varbinary(14) NOT NULL default '',
+ ipb_range_start tinyblob NOT NULL,
+ ipb_range_end tinyblob NOT NULL,
+ ipb_deleted bool NOT NULL default 0,
+ ipb_block_email bool NOT NULL default 0,
+ ipb_allow_usertalk bool NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ipb_address ON /*_*/ipblocks (ipb_address(255), ipb_user, ipb_auto, ipb_anon_only);
+CREATE INDEX /*i*/ipb_user ON /*_*/ipblocks (ipb_user);
+CREATE INDEX /*i*/ipb_range ON /*_*/ipblocks (ipb_range_start(8), ipb_range_end(8));
+CREATE INDEX /*i*/ipb_timestamp ON /*_*/ipblocks (ipb_timestamp);
+CREATE INDEX /*i*/ipb_expiry ON /*_*/ipblocks (ipb_expiry);
+CREATE TABLE /*_*/image (
+ img_name varchar(255) binary NOT NULL default '' PRIMARY KEY,
+ img_size int unsigned NOT NULL default 0,
+ img_width int NOT NULL default 0,
+ img_height int NOT NULL default 0,
+ img_metadata mediumblob NOT NULL,
+ img_bits int NOT NULL default 0,
+ img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+ img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") NOT NULL default "unknown",
+ img_minor_mime varbinary(100) NOT NULL default "unknown",
+ img_description tinyblob NOT NULL,
+ img_user int unsigned NOT NULL default 0,
+ img_user_text varchar(255) binary NOT NULL,
+ img_timestamp varbinary(14) NOT NULL default '',
+ img_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/img_usertext_timestamp ON /*_*/image (img_user_text,img_timestamp);
+CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
+CREATE INDEX /*i*/img_timestamp ON /*_*/image (img_timestamp);
+CREATE INDEX /*i*/img_sha1 ON /*_*/image (img_sha1);
+CREATE TABLE /*_*/oldimage (
+ oi_name varchar(255) binary NOT NULL default '',
+ oi_archive_name varchar(255) binary NOT NULL default '',
+ oi_size int unsigned NOT NULL default 0,
+ oi_width int NOT NULL default 0,
+ oi_height int NOT NULL default 0,
+ oi_bits int NOT NULL default 0,
+ oi_description tinyblob NOT NULL,
+ oi_user int unsigned NOT NULL default 0,
+ oi_user_text varchar(255) binary NOT NULL,
+ oi_timestamp binary(14) NOT NULL default '',
+ oi_metadata mediumblob NOT NULL,
+ oi_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+ oi_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") NOT NULL default "unknown",
+ oi_minor_mime varbinary(100) NOT NULL default "unknown",
+ oi_deleted tinyint unsigned NOT NULL default 0,
+ oi_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
+CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
+CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name,oi_archive_name(14));
+CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1);
+CREATE TABLE /*_*/filearchive (
+ fa_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ fa_name varchar(255) binary NOT NULL default '',
+ fa_archive_name varchar(255) binary default '',
+ fa_storage_group varbinary(16),
+ fa_storage_key varbinary(64) default '',
+ fa_deleted_user int,
+ fa_deleted_timestamp binary(14) default '',
+ fa_deleted_reason text,
+ fa_size int unsigned default 0,
+ fa_width int default 0,
+ fa_height int default 0,
+ fa_metadata mediumblob,
+ fa_bits int default 0,
+ fa_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+ fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") default "unknown",
+ fa_minor_mime varbinary(100) default "unknown",
+ fa_description tinyblob,
+ fa_user int unsigned default 0,
+ fa_user_text varchar(255) binary,
+ fa_timestamp binary(14) default '',
+ fa_deleted tinyint unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/fa_name ON /*_*/filearchive (fa_name, fa_timestamp);
+CREATE INDEX /*i*/fa_storage_group ON /*_*/filearchive (fa_storage_group, fa_storage_key);
+CREATE INDEX /*i*/fa_deleted_timestamp ON /*_*/filearchive (fa_deleted_timestamp);
+CREATE INDEX /*i*/fa_user_timestamp ON /*_*/filearchive (fa_user_text,fa_timestamp);
+CREATE TABLE /*_*/recentchanges (
+ rc_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ rc_timestamp varbinary(14) NOT NULL default '',
+ rc_cur_time varbinary(14) NOT NULL default '',
+ rc_user int unsigned NOT NULL default 0,
+ rc_user_text varchar(255) binary NOT NULL,
+ rc_namespace int NOT NULL default 0,
+ rc_title varchar(255) binary NOT NULL default '',
+ rc_comment varchar(255) binary NOT NULL default '',
+ rc_minor tinyint unsigned NOT NULL default 0,
+ rc_bot tinyint unsigned NOT NULL default 0,
+ rc_new tinyint unsigned NOT NULL default 0,
+ rc_cur_id int unsigned NOT NULL default 0,
+ rc_this_oldid int unsigned NOT NULL default 0,
+ rc_last_oldid int unsigned NOT NULL default 0,
+ rc_type tinyint unsigned NOT NULL default 0,
+ rc_moved_to_ns tinyint unsigned NOT NULL default 0,
+ rc_moved_to_title varchar(255) binary NOT NULL default '',
+ rc_patrolled tinyint unsigned NOT NULL default 0,
+ rc_ip varbinary(40) NOT NULL default '',
+ rc_old_len int,
+ rc_new_len int,
+ rc_deleted tinyint unsigned NOT NULL default 0,
+ rc_logid int unsigned NOT NULL default 0,
+ rc_log_type varbinary(255) NULL default NULL,
+ rc_log_action varbinary(255) NULL default NULL,
+ rc_params blob NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/rc_timestamp ON /*_*/recentchanges (rc_timestamp);
+CREATE INDEX /*i*/rc_namespace_title ON /*_*/recentchanges (rc_namespace, rc_title);
+CREATE INDEX /*i*/rc_cur_id ON /*_*/recentchanges (rc_cur_id);
+CREATE INDEX /*i*/new_name_timestamp ON /*_*/recentchanges (rc_new,rc_namespace,rc_timestamp);
+CREATE INDEX /*i*/rc_ip ON /*_*/recentchanges (rc_ip);
+CREATE INDEX /*i*/rc_ns_usertext ON /*_*/recentchanges (rc_namespace, rc_user_text);
+CREATE INDEX /*i*/rc_user_text ON /*_*/recentchanges (rc_user_text, rc_timestamp);
+CREATE TABLE /*_*/watchlist (
+ wl_user int unsigned NOT NULL,
+ wl_namespace int NOT NULL default 0,
+ wl_title varchar(255) binary NOT NULL default '',
+ wl_notificationtimestamp varbinary(14)
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/wl_user ON /*_*/watchlist (wl_user, wl_namespace, wl_title);
+CREATE INDEX /*i*/namespace_title ON /*_*/watchlist (wl_namespace, wl_title);
+CREATE TABLE /*_*/math (
+ math_inputhash varbinary(16) NOT NULL,
+ math_outputhash varbinary(16) NOT NULL,
+ math_html_conservativeness tinyint NOT NULL,
+ math_html text,
+ math_mathml text
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/math_inputhash ON /*_*/math (math_inputhash);
+CREATE TABLE /*_*/searchindex (
+ si_page int unsigned NOT NULL,
+ si_title varchar(255) NOT NULL default '',
+ si_text mediumtext NOT NULL
+) ENGINE=MyISAM;
+CREATE UNIQUE INDEX /*i*/si_page ON /*_*/searchindex (si_page);
+CREATE FULLTEXT INDEX /*i*/si_title ON /*_*/searchindex (si_title);
+CREATE FULLTEXT INDEX /*i*/si_text ON /*_*/searchindex (si_text);
+CREATE TABLE /*_*/interwiki (
+ iw_prefix varchar(32) NOT NULL,
+ iw_url blob NOT NULL,
+ iw_local bool NOT NULL,
+ iw_trans tinyint NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/iw_prefix ON /*_*/interwiki (iw_prefix);
+CREATE TABLE /*_*/querycache (
+ qc_type varbinary(32) NOT NULL,
+ qc_value int unsigned NOT NULL default 0,
+ qc_namespace int NOT NULL default 0,
+ qc_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/qc_type ON /*_*/querycache (qc_type,qc_value);
+CREATE TABLE /*_*/objectcache (
+ keyname varbinary(255) NOT NULL default '' PRIMARY KEY,
+ value mediumblob,
+ exptime datetime
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/exptime ON /*_*/objectcache (exptime);
+CREATE TABLE /*_*/transcache (
+ tc_url varbinary(255) NOT NULL,
+ tc_contents text,
+ tc_time binary(14) NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/tc_url_idx ON /*_*/transcache (tc_url);
+CREATE TABLE /*_*/logging (
+ log_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ log_type varbinary(32) NOT NULL default '',
+ log_action varbinary(32) NOT NULL default '',
+ log_timestamp binary(14) NOT NULL default '19700101000000',
+ log_user int unsigned NOT NULL default 0,
+ log_user_text varchar(255) binary NOT NULL default '',
+ log_namespace int NOT NULL default 0,
+ log_title varchar(255) binary NOT NULL default '',
+ log_page int unsigned NULL,
+ log_comment varchar(255) NOT NULL default '',
+ log_params blob NOT NULL,
+ log_deleted tinyint unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/type_time ON /*_*/logging (log_type, log_timestamp);
+CREATE INDEX /*i*/user_time ON /*_*/logging (log_user, log_timestamp);
+CREATE INDEX /*i*/page_time ON /*_*/logging (log_namespace, log_title, log_timestamp);
+CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp);
+CREATE INDEX /*i*/log_user_type_time ON /*_*/logging (log_user, log_type, log_timestamp);
+CREATE INDEX /*i*/log_page_id_time ON /*_*/logging (log_page,log_timestamp);
+CREATE TABLE /*_*/log_search (
+ ls_field varbinary(32) NOT NULL,
+ ls_value varchar(255) NOT NULL,
+ ls_log_id int unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ls_field_val ON /*_*/log_search (ls_field,ls_value,ls_log_id);
+CREATE INDEX /*i*/ls_log_id ON /*_*/log_search (ls_log_id);
+CREATE TABLE /*_*/trackbacks (
+ tb_id int PRIMARY KEY AUTO_INCREMENT,
+ tb_page int REFERENCES /*_*/page(page_id) ON DELETE CASCADE,
+ tb_title varchar(255) NOT NULL,
+ tb_url blob NOT NULL,
+ tb_ex text,
+ tb_name varchar(255)
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/tb_page ON /*_*/trackbacks (tb_page);
+CREATE TABLE /*_*/job (
+ job_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ job_cmd varbinary(60) NOT NULL default '',
+ job_namespace int NOT NULL,
+ job_title varchar(255) binary NOT NULL,
+ job_params blob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/job_cmd ON /*_*/job (job_cmd, job_namespace, job_title, job_params(128));
+CREATE TABLE /*_*/querycache_info (
+ qci_type varbinary(32) NOT NULL default '',
+ qci_timestamp binary(14) NOT NULL default '19700101000000'
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/qci_type ON /*_*/querycache_info (qci_type);
+CREATE TABLE /*_*/redirect (
+ rd_from int unsigned NOT NULL default 0 PRIMARY KEY,
+ rd_namespace int NOT NULL default 0,
+ rd_title varchar(255) binary NOT NULL default '',
+ rd_interwiki varchar(32) default NULL,
+ rd_fragment varchar(255) binary default NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/rd_ns_title ON /*_*/redirect (rd_namespace,rd_title,rd_from);
+CREATE TABLE /*_*/querycachetwo (
+ qcc_type varbinary(32) NOT NULL,
+ qcc_value int unsigned NOT NULL default 0,
+ qcc_namespace int NOT NULL default 0,
+ qcc_title varchar(255) binary NOT NULL default '',
+ qcc_namespacetwo int NOT NULL default 0,
+ qcc_titletwo varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/qcc_type ON /*_*/querycachetwo (qcc_type,qcc_value);
+CREATE INDEX /*i*/qcc_title ON /*_*/querycachetwo (qcc_type,qcc_namespace,qcc_title);
+CREATE INDEX /*i*/qcc_titletwo ON /*_*/querycachetwo (qcc_type,qcc_namespacetwo,qcc_titletwo);
+CREATE TABLE /*_*/page_restrictions (
+ pr_page int NOT NULL,
+ pr_type varbinary(60) NOT NULL,
+ pr_level varbinary(60) NOT NULL,
+ pr_cascade tinyint NOT NULL,
+ pr_user int NULL,
+ pr_expiry varbinary(14) NULL,
+ pr_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/pr_pagetype ON /*_*/page_restrictions (pr_page,pr_type);
+CREATE INDEX /*i*/pr_typelevel ON /*_*/page_restrictions (pr_type,pr_level);
+CREATE INDEX /*i*/pr_level ON /*_*/page_restrictions (pr_level);
+CREATE INDEX /*i*/pr_cascade ON /*_*/page_restrictions (pr_cascade);
+CREATE TABLE /*_*/protected_titles (
+ pt_namespace int NOT NULL,
+ pt_title varchar(255) binary NOT NULL,
+ pt_user int unsigned NOT NULL,
+ pt_reason tinyblob,
+ pt_timestamp binary(14) NOT NULL,
+ pt_expiry varbinary(14) NOT NULL default '',
+ pt_create_perm varbinary(60) NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/pt_namespace_title ON /*_*/protected_titles (pt_namespace,pt_title);
+CREATE INDEX /*i*/pt_timestamp ON /*_*/protected_titles (pt_timestamp);
+CREATE TABLE /*_*/page_props (
+ pp_page int NOT NULL,
+ pp_propname varbinary(60) NOT NULL,
+ pp_value blob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/pp_page_propname ON /*_*/page_props (pp_page,pp_propname);
+CREATE TABLE /*_*/updatelog (
+ ul_key varchar(255) NOT NULL PRIMARY KEY
+) /*$wgDBTableOptions*/;
+CREATE TABLE /*_*/change_tag (
+ ct_rc_id int NULL,
+ ct_log_id int NULL,
+ ct_rev_id int NULL,
+ ct_tag varchar(255) NOT NULL,
+ ct_params blob NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/change_tag_rc_tag ON /*_*/change_tag (ct_rc_id,ct_tag);
+CREATE UNIQUE INDEX /*i*/change_tag_log_tag ON /*_*/change_tag (ct_log_id,ct_tag);
+CREATE UNIQUE INDEX /*i*/change_tag_rev_tag ON /*_*/change_tag (ct_rev_id,ct_tag);
+CREATE INDEX /*i*/change_tag_tag_id ON /*_*/change_tag (ct_tag,ct_rc_id,ct_rev_id,ct_log_id);
+CREATE TABLE /*_*/tag_summary (
+ ts_rc_id int NULL,
+ ts_log_id int NULL,
+ ts_rev_id int NULL,
+ ts_tags blob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/tag_summary_rc_id ON /*_*/tag_summary (ts_rc_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_log_id ON /*_*/tag_summary (ts_log_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_rev_id ON /*_*/tag_summary (ts_rev_id);
+CREATE TABLE /*_*/valid_tag (
+ vt_tag varchar(255) NOT NULL PRIMARY KEY
+) /*$wgDBTableOptions*/;
+CREATE TABLE /*_*/l10n_cache (
+ lc_lang varbinary(32) NOT NULL,
+ lc_key varchar(255) NOT NULL,
+ lc_value mediumblob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/lc_lang_key ON /*_*/l10n_cache (lc_lang, lc_key);
diff --git a/tests/phpunit/includes/db/sqlite/tables-1.17.sql b/tests/phpunit/includes/db/sqlite/tables-1.17.sql
new file mode 100644
index 00000000..69ae3764
--- /dev/null
+++ b/tests/phpunit/includes/db/sqlite/tables-1.17.sql
@@ -0,0 +1,516 @@
+-- This is a copy of MediaWiki 1.17 schema shared by MySQL and SQLite.
+-- It is used for updater testing. Comments are stripped to decrease
+-- file size, as we don't need to maintain it.
+
+CREATE TABLE /*_*/user (
+ user_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ user_name varchar(255) binary NOT NULL default '',
+ user_real_name varchar(255) binary NOT NULL default '',
+ user_password tinyblob NOT NULL,
+ user_newpassword tinyblob NOT NULL,
+ user_newpass_time binary(14),
+ user_email tinytext NOT NULL,
+ user_options blob NOT NULL,
+ user_touched binary(14) NOT NULL default '',
+ user_token binary(32) NOT NULL default '',
+ user_email_authenticated binary(14),
+ user_email_token binary(32),
+ user_email_token_expires binary(14),
+ user_registration binary(14),
+ user_editcount int
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/user_name ON /*_*/user (user_name);
+CREATE INDEX /*i*/user_email_token ON /*_*/user (user_email_token);
+CREATE TABLE /*_*/user_groups (
+ ug_user int unsigned NOT NULL default 0,
+ ug_group varbinary(16) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ug_user_group ON /*_*/user_groups (ug_user,ug_group);
+CREATE INDEX /*i*/ug_group ON /*_*/user_groups (ug_group);
+CREATE TABLE /*_*/user_newtalk (
+ user_id int NOT NULL default 0,
+ user_ip varbinary(40) NOT NULL default '',
+ user_last_timestamp binary(14) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/un_user_id ON /*_*/user_newtalk (user_id);
+CREATE INDEX /*i*/un_user_ip ON /*_*/user_newtalk (user_ip);
+CREATE TABLE /*_*/user_properties (
+ up_user int NOT NULL,
+ up_property varbinary(32) NOT NULL,
+ up_value blob
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/user_properties_user_property ON /*_*/user_properties (up_user,up_property);
+CREATE INDEX /*i*/user_properties_property ON /*_*/user_properties (up_property);
+CREATE TABLE /*_*/page (
+ page_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ page_namespace int NOT NULL,
+ page_title varchar(255) binary NOT NULL,
+ page_restrictions tinyblob NOT NULL,
+ page_counter bigint unsigned NOT NULL default 0,
+ page_is_redirect tinyint unsigned NOT NULL default 0,
+ page_is_new tinyint unsigned NOT NULL default 0,
+ page_random real unsigned NOT NULL,
+ page_touched binary(14) NOT NULL default '',
+ page_latest int unsigned NOT NULL,
+ page_len int unsigned NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/name_title ON /*_*/page (page_namespace,page_title);
+CREATE INDEX /*i*/page_random ON /*_*/page (page_random);
+CREATE INDEX /*i*/page_len ON /*_*/page (page_len);
+CREATE TABLE /*_*/revision (
+ rev_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ rev_page int unsigned NOT NULL,
+ rev_text_id int unsigned NOT NULL,
+ rev_comment tinyblob NOT NULL,
+ rev_user int unsigned NOT NULL default 0,
+ rev_user_text varchar(255) binary NOT NULL default '',
+ rev_timestamp binary(14) NOT NULL default '',
+ rev_minor_edit tinyint unsigned NOT NULL default 0,
+ rev_deleted tinyint unsigned NOT NULL default 0,
+ rev_len int unsigned,
+ rev_parent_id int unsigned default NULL
+) /*$wgDBTableOptions*/ MAX_ROWS=10000000 AVG_ROW_LENGTH=1024;
+CREATE UNIQUE INDEX /*i*/rev_page_id ON /*_*/revision (rev_page, rev_id);
+CREATE INDEX /*i*/rev_timestamp ON /*_*/revision (rev_timestamp);
+CREATE INDEX /*i*/page_timestamp ON /*_*/revision (rev_page,rev_timestamp);
+CREATE INDEX /*i*/user_timestamp ON /*_*/revision (rev_user,rev_timestamp);
+CREATE INDEX /*i*/usertext_timestamp ON /*_*/revision (rev_user_text,rev_timestamp);
+CREATE TABLE /*_*/text (
+ old_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ old_text mediumblob NOT NULL,
+ old_flags tinyblob NOT NULL
+) /*$wgDBTableOptions*/ MAX_ROWS=10000000 AVG_ROW_LENGTH=10240;
+CREATE TABLE /*_*/archive (
+ ar_namespace int NOT NULL default 0,
+ ar_title varchar(255) binary NOT NULL default '',
+ ar_text mediumblob NOT NULL,
+ ar_comment tinyblob NOT NULL,
+ ar_user int unsigned NOT NULL default 0,
+ ar_user_text varchar(255) binary NOT NULL,
+ ar_timestamp binary(14) NOT NULL default '',
+ ar_minor_edit tinyint NOT NULL default 0,
+ ar_flags tinyblob NOT NULL,
+ ar_rev_id int unsigned,
+ ar_text_id int unsigned,
+ ar_deleted tinyint unsigned NOT NULL default 0,
+ ar_len int unsigned,
+ ar_page_id int unsigned,
+ ar_parent_id int unsigned default NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX /*i*/ar_usertext_timestamp ON /*_*/archive (ar_user_text,ar_timestamp);
+CREATE INDEX /*i*/ar_revid ON /*_*/archive (ar_rev_id);
+CREATE TABLE /*_*/pagelinks (
+ pl_from int unsigned NOT NULL default 0,
+ pl_namespace int NOT NULL default 0,
+ pl_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/pl_from ON /*_*/pagelinks (pl_from,pl_namespace,pl_title);
+CREATE UNIQUE INDEX /*i*/pl_namespace ON /*_*/pagelinks (pl_namespace,pl_title,pl_from);
+CREATE TABLE /*_*/templatelinks (
+ tl_from int unsigned NOT NULL default 0,
+ tl_namespace int NOT NULL default 0,
+ tl_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/tl_from ON /*_*/templatelinks (tl_from,tl_namespace,tl_title);
+CREATE UNIQUE INDEX /*i*/tl_namespace ON /*_*/templatelinks (tl_namespace,tl_title,tl_from);
+CREATE TABLE /*_*/imagelinks (
+ il_from int unsigned NOT NULL default 0,
+ il_to varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/il_from ON /*_*/imagelinks (il_from,il_to);
+CREATE UNIQUE INDEX /*i*/il_to ON /*_*/imagelinks (il_to,il_from);
+CREATE TABLE /*_*/categorylinks (
+ cl_from int unsigned NOT NULL default 0,
+ cl_to varchar(255) binary NOT NULL default '',
+ cl_sortkey varbinary(230) NOT NULL default '',
+ cl_sortkey_prefix varchar(255) binary NOT NULL default '',
+ cl_timestamp timestamp NOT NULL,
+ cl_collation varbinary(32) NOT NULL default '',
+ cl_type ENUM('page', 'subcat', 'file') NOT NULL default 'page'
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/cl_from ON /*_*/categorylinks (cl_from,cl_to);
+CREATE INDEX /*i*/cl_sortkey ON /*_*/categorylinks (cl_to,cl_type,cl_sortkey,cl_from);
+CREATE INDEX /*i*/cl_timestamp ON /*_*/categorylinks (cl_to,cl_timestamp);
+CREATE INDEX /*i*/cl_collation ON /*_*/categorylinks (cl_collation);
+CREATE TABLE /*_*/category (
+ cat_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ cat_title varchar(255) binary NOT NULL,
+ cat_pages int signed NOT NULL default 0,
+ cat_subcats int signed NOT NULL default 0,
+ cat_files int signed NOT NULL default 0,
+ cat_hidden tinyint unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/cat_title ON /*_*/category (cat_title);
+CREATE INDEX /*i*/cat_pages ON /*_*/category (cat_pages);
+CREATE TABLE /*_*/externallinks (
+ el_from int unsigned NOT NULL default 0,
+ el_to blob NOT NULL,
+ el_index blob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/el_from ON /*_*/externallinks (el_from, el_to(40));
+CREATE INDEX /*i*/el_to ON /*_*/externallinks (el_to(60), el_from);
+CREATE INDEX /*i*/el_index ON /*_*/externallinks (el_index(60));
+CREATE TABLE /*_*/external_user (
+ eu_local_id int unsigned NOT NULL PRIMARY KEY,
+ eu_external_id varchar(255) binary NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/eu_external_id ON /*_*/external_user (eu_external_id);
+CREATE TABLE /*_*/langlinks (
+ ll_from int unsigned NOT NULL default 0,
+ ll_lang varbinary(20) NOT NULL default '',
+ ll_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ll_from ON /*_*/langlinks (ll_from, ll_lang);
+CREATE INDEX /*i*/ll_lang ON /*_*/langlinks (ll_lang, ll_title);
+CREATE TABLE /*_*/iwlinks (
+ iwl_from int unsigned NOT NULL default 0,
+ iwl_prefix varbinary(20) NOT NULL default '',
+ iwl_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/iwl_from ON /*_*/iwlinks (iwl_from, iwl_prefix, iwl_title);
+CREATE UNIQUE INDEX /*i*/iwl_prefix_title_from ON /*_*/iwlinks (iwl_prefix, iwl_title, iwl_from);
+CREATE TABLE /*_*/site_stats (
+ ss_row_id int unsigned NOT NULL,
+ ss_total_views bigint unsigned default 0,
+ ss_total_edits bigint unsigned default 0,
+ ss_good_articles bigint unsigned default 0,
+ ss_total_pages bigint default '-1',
+ ss_users bigint default '-1',
+ ss_active_users bigint default '-1',
+ ss_admins int default '-1',
+ ss_images int default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ss_row_id ON /*_*/site_stats (ss_row_id);
+CREATE TABLE /*_*/hitcounter (
+ hc_id int unsigned NOT NULL
+) ENGINE=HEAP MAX_ROWS=25000;
+CREATE TABLE /*_*/ipblocks (
+ ipb_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ ipb_address tinyblob NOT NULL,
+ ipb_user int unsigned NOT NULL default 0,
+ ipb_by int unsigned NOT NULL default 0,
+ ipb_by_text varchar(255) binary NOT NULL default '',
+ ipb_reason tinyblob NOT NULL,
+ ipb_timestamp binary(14) NOT NULL default '',
+ ipb_auto bool NOT NULL default 0,
+ ipb_anon_only bool NOT NULL default 0,
+ ipb_create_account bool NOT NULL default 1,
+ ipb_enable_autoblock bool NOT NULL default '1',
+ ipb_expiry varbinary(14) NOT NULL default '',
+ ipb_range_start tinyblob NOT NULL,
+ ipb_range_end tinyblob NOT NULL,
+ ipb_deleted bool NOT NULL default 0,
+ ipb_block_email bool NOT NULL default 0,
+ ipb_allow_usertalk bool NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ipb_address ON /*_*/ipblocks (ipb_address(255), ipb_user, ipb_auto, ipb_anon_only);
+CREATE INDEX /*i*/ipb_user ON /*_*/ipblocks (ipb_user);
+CREATE INDEX /*i*/ipb_range ON /*_*/ipblocks (ipb_range_start(8), ipb_range_end(8));
+CREATE INDEX /*i*/ipb_timestamp ON /*_*/ipblocks (ipb_timestamp);
+CREATE INDEX /*i*/ipb_expiry ON /*_*/ipblocks (ipb_expiry);
+CREATE TABLE /*_*/image (
+ img_name varchar(255) binary NOT NULL default '' PRIMARY KEY,
+ img_size int unsigned NOT NULL default 0,
+ img_width int NOT NULL default 0,
+ img_height int NOT NULL default 0,
+ img_metadata mediumblob NOT NULL,
+ img_bits int NOT NULL default 0,
+ img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+ img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") NOT NULL default "unknown",
+ img_minor_mime varbinary(100) NOT NULL default "unknown",
+ img_description tinyblob NOT NULL,
+ img_user int unsigned NOT NULL default 0,
+ img_user_text varchar(255) binary NOT NULL,
+ img_timestamp varbinary(14) NOT NULL default '',
+ img_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/img_usertext_timestamp ON /*_*/image (img_user_text,img_timestamp);
+CREATE INDEX /*i*/img_size ON /*_*/image (img_size);
+CREATE INDEX /*i*/img_timestamp ON /*_*/image (img_timestamp);
+CREATE INDEX /*i*/img_sha1 ON /*_*/image (img_sha1);
+CREATE TABLE /*_*/oldimage (
+ oi_name varchar(255) binary NOT NULL default '',
+ oi_archive_name varchar(255) binary NOT NULL default '',
+ oi_size int unsigned NOT NULL default 0,
+ oi_width int NOT NULL default 0,
+ oi_height int NOT NULL default 0,
+ oi_bits int NOT NULL default 0,
+ oi_description tinyblob NOT NULL,
+ oi_user int unsigned NOT NULL default 0,
+ oi_user_text varchar(255) binary NOT NULL,
+ oi_timestamp binary(14) NOT NULL default '',
+ oi_metadata mediumblob NOT NULL,
+ oi_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+ oi_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") NOT NULL default "unknown",
+ oi_minor_mime varbinary(100) NOT NULL default "unknown",
+ oi_deleted tinyint unsigned NOT NULL default 0,
+ oi_sha1 varbinary(32) NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
+CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
+CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name,oi_archive_name(14));
+CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1);
+CREATE TABLE /*_*/filearchive (
+ fa_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ fa_name varchar(255) binary NOT NULL default '',
+ fa_archive_name varchar(255) binary default '',
+ fa_storage_group varbinary(16),
+ fa_storage_key varbinary(64) default '',
+ fa_deleted_user int,
+ fa_deleted_timestamp binary(14) default '',
+ fa_deleted_reason text,
+ fa_size int unsigned default 0,
+ fa_width int default 0,
+ fa_height int default 0,
+ fa_metadata mediumblob,
+ fa_bits int default 0,
+ fa_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
+ fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") default "unknown",
+ fa_minor_mime varbinary(100) default "unknown",
+ fa_description tinyblob,
+ fa_user int unsigned default 0,
+ fa_user_text varchar(255) binary,
+ fa_timestamp binary(14) default '',
+ fa_deleted tinyint unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/fa_name ON /*_*/filearchive (fa_name, fa_timestamp);
+CREATE INDEX /*i*/fa_storage_group ON /*_*/filearchive (fa_storage_group, fa_storage_key);
+CREATE INDEX /*i*/fa_deleted_timestamp ON /*_*/filearchive (fa_deleted_timestamp);
+CREATE INDEX /*i*/fa_user_timestamp ON /*_*/filearchive (fa_user_text,fa_timestamp);
+CREATE TABLE /*_*/recentchanges (
+ rc_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ rc_timestamp varbinary(14) NOT NULL default '',
+ rc_cur_time varbinary(14) NOT NULL default '',
+ rc_user int unsigned NOT NULL default 0,
+ rc_user_text varchar(255) binary NOT NULL,
+ rc_namespace int NOT NULL default 0,
+ rc_title varchar(255) binary NOT NULL default '',
+ rc_comment varchar(255) binary NOT NULL default '',
+ rc_minor tinyint unsigned NOT NULL default 0,
+ rc_bot tinyint unsigned NOT NULL default 0,
+ rc_new tinyint unsigned NOT NULL default 0,
+ rc_cur_id int unsigned NOT NULL default 0,
+ rc_this_oldid int unsigned NOT NULL default 0,
+ rc_last_oldid int unsigned NOT NULL default 0,
+ rc_type tinyint unsigned NOT NULL default 0,
+ rc_moved_to_ns tinyint unsigned NOT NULL default 0,
+ rc_moved_to_title varchar(255) binary NOT NULL default '',
+ rc_patrolled tinyint unsigned NOT NULL default 0,
+ rc_ip varbinary(40) NOT NULL default '',
+ rc_old_len int,
+ rc_new_len int,
+ rc_deleted tinyint unsigned NOT NULL default 0,
+ rc_logid int unsigned NOT NULL default 0,
+ rc_log_type varbinary(255) NULL default NULL,
+ rc_log_action varbinary(255) NULL default NULL,
+ rc_params blob NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/rc_timestamp ON /*_*/recentchanges (rc_timestamp);
+CREATE INDEX /*i*/rc_namespace_title ON /*_*/recentchanges (rc_namespace, rc_title);
+CREATE INDEX /*i*/rc_cur_id ON /*_*/recentchanges (rc_cur_id);
+CREATE INDEX /*i*/new_name_timestamp ON /*_*/recentchanges (rc_new,rc_namespace,rc_timestamp);
+CREATE INDEX /*i*/rc_ip ON /*_*/recentchanges (rc_ip);
+CREATE INDEX /*i*/rc_ns_usertext ON /*_*/recentchanges (rc_namespace, rc_user_text);
+CREATE INDEX /*i*/rc_user_text ON /*_*/recentchanges (rc_user_text, rc_timestamp);
+CREATE TABLE /*_*/watchlist (
+ wl_user int unsigned NOT NULL,
+ wl_namespace int NOT NULL default 0,
+ wl_title varchar(255) binary NOT NULL default '',
+ wl_notificationtimestamp varbinary(14)
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/wl_user ON /*_*/watchlist (wl_user, wl_namespace, wl_title);
+CREATE INDEX /*i*/namespace_title ON /*_*/watchlist (wl_namespace, wl_title);
+CREATE TABLE /*_*/math (
+ math_inputhash varbinary(16) NOT NULL,
+ math_outputhash varbinary(16) NOT NULL,
+ math_html_conservativeness tinyint NOT NULL,
+ math_html text,
+ math_mathml text
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/math_inputhash ON /*_*/math (math_inputhash);
+CREATE TABLE /*_*/searchindex (
+ si_page int unsigned NOT NULL,
+ si_title varchar(255) NOT NULL default '',
+ si_text mediumtext NOT NULL
+) ENGINE=MyISAM;
+CREATE UNIQUE INDEX /*i*/si_page ON /*_*/searchindex (si_page);
+CREATE FULLTEXT INDEX /*i*/si_title ON /*_*/searchindex (si_title);
+CREATE FULLTEXT INDEX /*i*/si_text ON /*_*/searchindex (si_text);
+CREATE TABLE /*_*/interwiki (
+ iw_prefix varchar(32) NOT NULL,
+ iw_url blob NOT NULL,
+ iw_api blob NOT NULL,
+ iw_wikiid varchar(64) NOT NULL,
+ iw_local bool NOT NULL,
+ iw_trans tinyint NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/iw_prefix ON /*_*/interwiki (iw_prefix);
+CREATE TABLE /*_*/querycache (
+ qc_type varbinary(32) NOT NULL,
+ qc_value int unsigned NOT NULL default 0,
+ qc_namespace int NOT NULL default 0,
+ qc_title varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/qc_type ON /*_*/querycache (qc_type,qc_value);
+CREATE TABLE /*_*/objectcache (
+ keyname varbinary(255) NOT NULL default '' PRIMARY KEY,
+ value mediumblob,
+ exptime datetime
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/exptime ON /*_*/objectcache (exptime);
+CREATE TABLE /*_*/transcache (
+ tc_url varbinary(255) NOT NULL,
+ tc_contents text,
+ tc_time binary(14) NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/tc_url_idx ON /*_*/transcache (tc_url);
+CREATE TABLE /*_*/logging (
+ log_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ log_type varbinary(32) NOT NULL default '',
+ log_action varbinary(32) NOT NULL default '',
+ log_timestamp binary(14) NOT NULL default '19700101000000',
+ log_user int unsigned NOT NULL default 0,
+ log_user_text varchar(255) binary NOT NULL default '',
+ log_namespace int NOT NULL default 0,
+ log_title varchar(255) binary NOT NULL default '',
+ log_page int unsigned NULL,
+ log_comment varchar(255) NOT NULL default '',
+ log_params blob NOT NULL,
+ log_deleted tinyint unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/type_time ON /*_*/logging (log_type, log_timestamp);
+CREATE INDEX /*i*/user_time ON /*_*/logging (log_user, log_timestamp);
+CREATE INDEX /*i*/page_time ON /*_*/logging (log_namespace, log_title, log_timestamp);
+CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp);
+CREATE INDEX /*i*/log_user_type_time ON /*_*/logging (log_user, log_type, log_timestamp);
+CREATE INDEX /*i*/log_page_id_time ON /*_*/logging (log_page,log_timestamp);
+CREATE TABLE /*_*/log_search (
+ ls_field varbinary(32) NOT NULL,
+ ls_value varchar(255) NOT NULL,
+ ls_log_id int unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ls_field_val ON /*_*/log_search (ls_field,ls_value,ls_log_id);
+CREATE INDEX /*i*/ls_log_id ON /*_*/log_search (ls_log_id);
+CREATE TABLE /*_*/trackbacks (
+ tb_id int PRIMARY KEY AUTO_INCREMENT,
+ tb_page int REFERENCES /*_*/page(page_id) ON DELETE CASCADE,
+ tb_title varchar(255) NOT NULL,
+ tb_url blob NOT NULL,
+ tb_ex text,
+ tb_name varchar(255)
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/tb_page ON /*_*/trackbacks (tb_page);
+CREATE TABLE /*_*/job (
+ job_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ job_cmd varbinary(60) NOT NULL default '',
+ job_namespace int NOT NULL,
+ job_title varchar(255) binary NOT NULL,
+ job_params blob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/job_cmd ON /*_*/job (job_cmd, job_namespace, job_title, job_params(128));
+CREATE TABLE /*_*/querycache_info (
+ qci_type varbinary(32) NOT NULL default '',
+ qci_timestamp binary(14) NOT NULL default '19700101000000'
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/qci_type ON /*_*/querycache_info (qci_type);
+CREATE TABLE /*_*/redirect (
+ rd_from int unsigned NOT NULL default 0 PRIMARY KEY,
+ rd_namespace int NOT NULL default 0,
+ rd_title varchar(255) binary NOT NULL default '',
+ rd_interwiki varchar(32) default NULL,
+ rd_fragment varchar(255) binary default NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/rd_ns_title ON /*_*/redirect (rd_namespace,rd_title,rd_from);
+CREATE TABLE /*_*/querycachetwo (
+ qcc_type varbinary(32) NOT NULL,
+ qcc_value int unsigned NOT NULL default 0,
+ qcc_namespace int NOT NULL default 0,
+ qcc_title varchar(255) binary NOT NULL default '',
+ qcc_namespacetwo int NOT NULL default 0,
+ qcc_titletwo varchar(255) binary NOT NULL default ''
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/qcc_type ON /*_*/querycachetwo (qcc_type,qcc_value);
+CREATE INDEX /*i*/qcc_title ON /*_*/querycachetwo (qcc_type,qcc_namespace,qcc_title);
+CREATE INDEX /*i*/qcc_titletwo ON /*_*/querycachetwo (qcc_type,qcc_namespacetwo,qcc_titletwo);
+CREATE TABLE /*_*/page_restrictions (
+ pr_page int NOT NULL,
+ pr_type varbinary(60) NOT NULL,
+ pr_level varbinary(60) NOT NULL,
+ pr_cascade tinyint NOT NULL,
+ pr_user int NULL,
+ pr_expiry varbinary(14) NULL,
+ pr_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/pr_pagetype ON /*_*/page_restrictions (pr_page,pr_type);
+CREATE INDEX /*i*/pr_typelevel ON /*_*/page_restrictions (pr_type,pr_level);
+CREATE INDEX /*i*/pr_level ON /*_*/page_restrictions (pr_level);
+CREATE INDEX /*i*/pr_cascade ON /*_*/page_restrictions (pr_cascade);
+CREATE TABLE /*_*/protected_titles (
+ pt_namespace int NOT NULL,
+ pt_title varchar(255) binary NOT NULL,
+ pt_user int unsigned NOT NULL,
+ pt_reason tinyblob,
+ pt_timestamp binary(14) NOT NULL,
+ pt_expiry varbinary(14) NOT NULL default '',
+ pt_create_perm varbinary(60) NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/pt_namespace_title ON /*_*/protected_titles (pt_namespace,pt_title);
+CREATE INDEX /*i*/pt_timestamp ON /*_*/protected_titles (pt_timestamp);
+CREATE TABLE /*_*/page_props (
+ pp_page int NOT NULL,
+ pp_propname varbinary(60) NOT NULL,
+ pp_value blob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/pp_page_propname ON /*_*/page_props (pp_page,pp_propname);
+CREATE TABLE /*_*/updatelog (
+ ul_key varchar(255) NOT NULL PRIMARY KEY,
+ ul_value blob
+) /*$wgDBTableOptions*/;
+CREATE TABLE /*_*/change_tag (
+ ct_rc_id int NULL,
+ ct_log_id int NULL,
+ ct_rev_id int NULL,
+ ct_tag varchar(255) NOT NULL,
+ ct_params blob NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/change_tag_rc_tag ON /*_*/change_tag (ct_rc_id,ct_tag);
+CREATE UNIQUE INDEX /*i*/change_tag_log_tag ON /*_*/change_tag (ct_log_id,ct_tag);
+CREATE UNIQUE INDEX /*i*/change_tag_rev_tag ON /*_*/change_tag (ct_rev_id,ct_tag);
+CREATE INDEX /*i*/change_tag_tag_id ON /*_*/change_tag (ct_tag,ct_rc_id,ct_rev_id,ct_log_id);
+CREATE TABLE /*_*/tag_summary (
+ ts_rc_id int NULL,
+ ts_log_id int NULL,
+ ts_rev_id int NULL,
+ ts_tags blob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/tag_summary_rc_id ON /*_*/tag_summary (ts_rc_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_log_id ON /*_*/tag_summary (ts_log_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_rev_id ON /*_*/tag_summary (ts_rev_id);
+CREATE TABLE /*_*/valid_tag (
+ vt_tag varchar(255) NOT NULL PRIMARY KEY
+) /*$wgDBTableOptions*/;
+CREATE TABLE /*_*/l10n_cache (
+ lc_lang varbinary(32) NOT NULL,
+ lc_key varchar(255) NOT NULL,
+ lc_value mediumblob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/lc_lang_key ON /*_*/l10n_cache (lc_lang, lc_key);
+CREATE TABLE /*_*/msg_resource (
+ mr_resource varbinary(255) NOT NULL,
+ mr_lang varbinary(32) NOT NULL,
+ mr_blob mediumblob NOT NULL,
+ mr_timestamp binary(14) NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/mr_resource_lang ON /*_*/msg_resource (mr_resource, mr_lang);
+CREATE TABLE /*_*/msg_resource_links (
+ mrl_resource varbinary(255) NOT NULL,
+ mrl_message varbinary(255) NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/mrl_message_resource ON /*_*/msg_resource_links (mrl_message, mrl_resource);
+CREATE TABLE /*_*/module_deps (
+ md_module varbinary(255) NOT NULL,
+ md_skin varbinary(32) NOT NULL,
+ md_deps mediumblob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/md_module_skin ON /*_*/module_deps (md_module, md_skin);
diff --git a/tests/phpunit/includes/installer/InstallDocFormatterTest.php b/tests/phpunit/includes/installer/InstallDocFormatterTest.php
new file mode 100644
index 00000000..56485d3e
--- /dev/null
+++ b/tests/phpunit/includes/installer/InstallDocFormatterTest.php
@@ -0,0 +1,64 @@
+<?php
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+class InstallDocFormatterTest extends MediaWikiTestCase {
+ /**
+ * @covers InstallDocFormatter::format
+ * @dataProvider provideDocFormattingTests
+ */
+ function testFormat( $expected, $unformattedText, $message = '' ) {
+ $this->assertEquals(
+ $expected,
+ InstallDocFormatter::format( $unformattedText ),
+ $message
+ );
+ }
+
+ /**
+ * Provider for testFormat()
+ */
+ function provideDocFormattingTests() {
+ # Format: (expected string, unformattedText string, optional message)
+ return array(
+ # Escape some wikitext
+ array( 'Install &lt;tag>' , 'Install <tag>', 'Escaping <' ),
+ array( 'Install &#123;&#123;template}}' , 'Install {{template}}', 'Escaping [[' ),
+ array( 'Install &#91;&#91;page]]' , 'Install [[page]]', 'Escaping {{' ),
+ array( 'Install ' , "Install \r", 'Removing \r' ),
+
+ # Transform \t{1,2} into :{1,2}
+ array( ':One indentation', "\tOne indentation", 'Replacing a single \t' ),
+ array( '::Two indentations', "\t\tTwo indentations", 'Replacing 2 x \t' ),
+
+ # Transform 'bug 123' links
+ array(
+ '<span class="config-plainlink">[https://bugzilla.wikimedia.org/123 bug 123]</span>',
+ 'bug 123', 'Testing bug 123 links' ),
+ array(
+ '(<span class="config-plainlink">[https://bugzilla.wikimedia.org/987654 bug 987654]</span>)',
+ '(bug 987654)', 'Testing (bug 987654) links' ),
+
+ # "bug abc" shouldn't work
+ array( 'bug foobar', 'bug foobar', "Don't match bug followed by non-digits" ),
+ array( 'bug !!fakefake!!', 'bug !!fakefake!!', "Don't match bug followed by non-digits" ),
+
+ # Transform '$wgFooBar' links
+ array(
+ '<span class="config-plainlink">[http://www.mediawiki.org/wiki/Manual:$wgFooBar $wgFooBar]</span>',
+ '$wgFooBar', 'Testing basic $wgFooBar' ),
+ array(
+ '<span class="config-plainlink">[http://www.mediawiki.org/wiki/Manual:$wgFooBar45 $wgFooBar45]</span>',
+ '$wgFooBar45', 'Testing $wgFooBar45 (with numbers)' ),
+ array(
+ '<span class="config-plainlink">[http://www.mediawiki.org/wiki/Manual:$wgFoo_Bar $wgFoo_Bar]</span>',
+ '$wgFoo_Bar', 'Testing $wgFoo_Bar (with underscore)' ),
+
+ # Icky variables that shouldn't link
+ array( '$myAwesomeVariable', '$myAwesomeVariable', 'Testing $myAwesomeVariable (not starting with $wg)' ),
+ array( '$()not!a&Var', '$()not!a&Var', 'Testing $()not!a&Var (obviously not a variable)' ),
+ );
+ }
+}
diff --git a/tests/phpunit/includes/libs/IEUrlExtensionTest.php b/tests/phpunit/includes/libs/IEUrlExtensionTest.php
new file mode 100644
index 00000000..c6270e90
--- /dev/null
+++ b/tests/phpunit/includes/libs/IEUrlExtensionTest.php
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * Tests for IEUrlExtension::findIE6Extension
+ */
+class IEUrlExtensionTest extends MediaWikiTestCase {
+ function testSimple() {
+ $this->assertEquals(
+ 'y',
+ IEUrlExtension::findIE6Extension( 'x.y' ),
+ 'Simple extension'
+ );
+ }
+
+ function testSimpleNoExt() {
+ $this->assertEquals(
+ '',
+ IEUrlExtension::findIE6Extension( 'x' ),
+ 'No extension'
+ );
+ }
+
+ function testEmpty() {
+ $this->assertEquals(
+ '',
+ IEUrlExtension::findIE6Extension( '' ),
+ 'Empty string'
+ );
+ }
+
+ function testQuestionMark() {
+ $this->assertEquals(
+ '',
+ IEUrlExtension::findIE6Extension( '?' ),
+ 'Question mark only'
+ );
+ }
+
+ function testExtQuestionMark() {
+ $this->assertEquals(
+ 'x',
+ IEUrlExtension::findIE6Extension( '.x?' ),
+ 'Extension then question mark'
+ );
+ }
+
+ function testQuestionMarkExt() {
+ $this->assertEquals(
+ 'x',
+ IEUrlExtension::findIE6Extension( '?.x' ),
+ 'Question mark then extension'
+ );
+ }
+
+ function testInvalidChar() {
+ $this->assertEquals(
+ '',
+ IEUrlExtension::findIE6Extension( '.x*' ),
+ 'Extension with invalid character'
+ );
+ }
+
+ function testInvalidCharThenExtension() {
+ $this->assertEquals(
+ 'x',
+ IEUrlExtension::findIE6Extension( '*.x' ),
+ 'Invalid character followed by an extension'
+ );
+ }
+
+ function testMultipleQuestionMarks() {
+ $this->assertEquals(
+ 'c',
+ IEUrlExtension::findIE6Extension( 'a?b?.c?.d?e?f' ),
+ 'Multiple question marks'
+ );
+ }
+
+ function testExeException() {
+ $this->assertEquals(
+ 'd',
+ IEUrlExtension::findIE6Extension( 'a?b?.exe?.d?.e' ),
+ '.exe exception'
+ );
+ }
+
+ function testExeException2() {
+ $this->assertEquals(
+ 'exe',
+ IEUrlExtension::findIE6Extension( 'a?b?.exe' ),
+ '.exe exception 2'
+ );
+ }
+
+ function testHash() {
+ $this->assertEquals(
+ '',
+ IEUrlExtension::findIE6Extension( 'a#b.c' ),
+ 'Hash character preceding extension'
+ );
+ }
+
+ function testHash2() {
+ $this->assertEquals(
+ '',
+ IEUrlExtension::findIE6Extension( 'a?#b.c' ),
+ 'Hash character preceding extension 2'
+ );
+ }
+
+ function testDotAtEnd() {
+ $this->assertEquals(
+ '',
+ IEUrlExtension::findIE6Extension( '.' ),
+ 'Dot at end of string'
+ );
+ }
+}
diff --git a/tests/phpunit/includes/libs/JavaScriptMinifierTest.php b/tests/phpunit/includes/libs/JavaScriptMinifierTest.php
new file mode 100644
index 00000000..aa05500e
--- /dev/null
+++ b/tests/phpunit/includes/libs/JavaScriptMinifierTest.php
@@ -0,0 +1,105 @@
+<?php
+
+class JavaScriptMinifierTest extends MediaWikiTestCase {
+
+ function provideCases() {
+ return array(
+ // Basic tokens
+ array( "\r\t\f \v\n\r", "" ),
+ array( "/* Foo *\n*bar\n*/", "" ),
+ /**
+ * ' Foo \' bar \
+ * baz \' quox ' .
+ */
+ array( "' Foo \\' bar \\\n baz \\' quox ' .length", "' Foo \\' bar \\\n baz \\' quox '.length" ),
+ array( "\" Foo \\\" bar \\\n baz \\\" quox \" .length", "\" Foo \\\" bar \\\n baz \\\" quox \".length" ),
+ array( "// Foo b/ar baz", "" ),
+ array( "/ Foo \\/ bar [ / \\] / ] baz / .length", "/ Foo \\/ bar [ / \\] / ] baz /.length" ),
+ // HTML comments
+ array( "<!-- Foo bar", "" ),
+ array( "<!-- Foo --> bar", "" ),
+ array( "--> Foo", "" ),
+ array( "x --> y", "x-->y" ),
+ // Semicolon insertion
+ array( "(function(){return\nx;})", "(function(){return\nx;})" ),
+ array( "throw\nx;", "throw\nx;" ),
+ array( "while(p){continue\nx;}", "while(p){continue\nx;}" ),
+ array( "while(p){break\nx;}", "while(p){break\nx;}" ),
+ array( "var\nx;", "var x;" ),
+ array( "x\ny;", "x\ny;" ),
+ array( "x\n++y;", "x\n++y;" ),
+ array( "x\n!y;", "x\n!y;" ),
+ array( "x\n{y}", "x\n{y}" ),
+ array( "x\n+y;", "x+y;" ),
+ array( "x\n(y);", "x(y);" ),
+ array( "5.\nx;", "5.\nx;" ),
+ array( "0xFF.\nx;", "0xFF.x;" ),
+ array( "5.3.\nx;", "5.3.x;" ),
+ // Token separation
+ array( "x in y", "x in y" ),
+ array( "/x/g in y", "/x/g in y" ),
+ array( "x in 30", "x in 30" ),
+ array( "x + ++ y", "x+ ++y" ),
+ array( "x / /y/.exec(z)", "x/ /y/.exec(z)" ),
+ // State machine
+ array( "/ x/g", "/ x/g" ),
+ array( "(function(){return/ x/g})", "(function(){return/ x/g})" ),
+ array( "+/ x/g", "+/ x/g" ),
+ array( "++/ x/g", "++/ x/g" ),
+ array( "x/ x/g", "x/x/g" ),
+ array( "(/ x/g)", "(/ x/g)" ),
+ array( "if(/ x/g);", "if(/ x/g);" ),
+ array( "(x/ x/g)", "(x/x/g)" ),
+ array( "([/ x/g])", "([/ x/g])" ),
+ array( "+x/ x/g", "+x/x/g" ),
+ array( "{}/ x/g", "{}/ x/g" ),
+ array( "+{}/ x/g", "+{}/x/g" ),
+ array( "(x)/ x/g", "(x)/x/g" ),
+ array( "if(x)/ x/g", "if(x)/ x/g" ),
+ array( "for(x;x;{}/ x/g);", "for(x;x;{}/x/g);" ),
+ array( "x;x;{}/ x/g", "x;x;{}/ x/g" ),
+ array( "x:{}/ x/g", "x:{}/ x/g" ),
+ array( "switch(x){case y?z:{}/ x/g:{}/ x/g;}", "switch(x){case y?z:{}/x/g:{}/ x/g;}" ),
+ array( "function x(){}/ x/g", "function x(){}/ x/g" ),
+ array( "+function x(){}/ x/g", "+function x(){}/x/g" ),
+
+ // Tests for things that broke in the past
+ // Multiline quoted string
+ array( "var foo=\"\\\nblah\\\n\";", "var foo=\"\\\nblah\\\n\";" ),
+ // Multiline quoted string followed by string with spaces
+ array( "var foo=\"\\\nblah\\\n\";\nvar baz = \" foo \";\n", "var foo=\"\\\nblah\\\n\";var baz=\" foo \";" ),
+ // URL in quoted string ( // is not a comment)
+ array( "aNode.setAttribute('href','http://foo.bar.org/baz');", "aNode.setAttribute('href','http://foo.bar.org/baz');" ),
+ // URL in quoted string after multiline quoted string
+ array( "var foo=\"\\\nblah\\\n\";\naNode.setAttribute('href','http://foo.bar.org/baz');", "var foo=\"\\\nblah\\\n\";aNode.setAttribute('href','http://foo.bar.org/baz');" ),
+ // Division vs. regex nastiness
+ array( "alert( (10+10) / '/'.charCodeAt( 0 ) + '//' );", "alert((10+10)/'/'.charCodeAt(0)+'//');" ),
+ array( "if(1)/a /g.exec('Pa ss');", "if(1)/a /g.exec('Pa ss');" ),
+
+ // newline insertion after 1000 chars: break after the "++", not before
+ array( str_repeat( ';', 996 ) . "if(x++);", str_repeat( ';', 996 ) . "if(x++\n);" ),
+
+ // Unicode letter characters should pass through ok in identifiers (bug 31187)
+ array( "var KaÅSkatolVal = {}", 'var KaÅSkatolVal={}'),
+ // And also per spec unicode char escape values should work in identifiers,
+ // as long as it's a valid char. In future it might get normalized.
+ array( "var Ka\\u015dSkatolVal = {}", 'var Ka\\u015dSkatolVal={}'),
+ );
+ }
+
+ /**
+ * @dataProvider provideCases
+ */
+ function testJavaScriptMinifierOutput( $code, $expectedOutput ) {
+ $minified = JavaScriptMinifier::minify( $code );
+
+ // JSMin+'s parser will throw an exception if output is not valid JS.
+ // suppression of warnings needed for stupid crap
+ wfSuppressWarnings();
+ $parser = new JSParser();
+ wfRestoreWarnings();
+ $parser->parse( $minified, 'minify-test.js', 1 );
+
+ $this->assertEquals( $expectedOutput, $minified, "Minified output should be in the form expected." );
+ }
+}
diff --git a/tests/phpunit/includes/media/BitmapMetadataHandlerTest.php b/tests/phpunit/includes/media/BitmapMetadataHandlerTest.php
new file mode 100644
index 00000000..a0d5cd86
--- /dev/null
+++ b/tests/phpunit/includes/media/BitmapMetadataHandlerTest.php
@@ -0,0 +1,125 @@
+<?php
+class BitmapMetadataHandlerTest extends MediaWikiTestCase {
+
+ public function setUp() {
+ $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ }
+
+ /**
+ * Test if having conflicting metadata values from different
+ * types of metadata, that the right one takes precedence.
+ *
+ * Basically the file has IPTC and XMP metadata, the
+ * IPTC should override the XMP, except for the multilingual
+ * translation (to en) where XMP should win.
+ */
+ public function testMultilingualCascade() {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+
+ $meta = BitmapMetadataHandler::Jpeg( $this->filePath .
+ '/Xmp-exif-multilingual_test.jpg' );
+
+ $expected = array(
+ 'x-default' => 'right(iptc)',
+ 'en' => 'right translation',
+ '_type' => 'lang'
+ );
+
+ $this->assertArrayHasKey( 'ImageDescription', $meta,
+ 'Did not extract any ImageDescription info?!' );
+
+ $this->assertEquals( $expected, $meta['ImageDescription'] );
+ }
+
+ /**
+ * Test for jpeg comments are being handled by
+ * BitmapMetadataHandler correctly.
+ *
+ * There's more extensive tests of comment extraction in
+ * JpegMetadataExtractorTests.php
+ */
+ public function testJpegComment() {
+ $meta = BitmapMetadataHandler::Jpeg( $this->filePath .
+ 'jpeg-comment-utf.jpg' );
+
+ $this->assertEquals( 'UTF-8 JPEG Comment — ¼',
+ $meta['JPEGFileComment'][0] );
+ }
+
+ public function testIPTCDates() {
+ $meta = BitmapMetadataHandler::Jpeg( $this->filePath .
+ 'iptc-timetest.jpg' );
+
+ $this->assertEquals( '2020:07:14 01:36:05', $meta['DateTimeDigitized'] );
+ $this->assertEquals( '1997:03:02 00:01:02', $meta['DateTimeOriginal'] );
+ }
+ /* File has an invalid time (+ one valid but really weird time)
+ * that shouldn't be included
+ */
+ public function testIPTCDatesInvalid() {
+ $meta = BitmapMetadataHandler::Jpeg( $this->filePath .
+ 'iptc-timetest-invalid.jpg' );
+
+ $this->assertEquals( '1845:03:02 00:01:02', $meta['DateTimeOriginal'] );
+ $this->assertFalse( isset( $meta['DateTimeDigitized'] ) );
+ }
+
+ /**
+ * XMP data should take priority over iptc data
+ * when hash has been updated, but not when
+ * the hash is wrong.
+ */
+ public function testMerging() {
+ $merger = new BitmapMetadataHandler();
+ $merger->addMetadata( array( 'foo' => 'xmp' ), 'xmp-general' );
+ $merger->addMetadata( array( 'bar' => 'xmp' ), 'xmp-general' );
+ $merger->addMetadata( array( 'baz' => 'xmp' ), 'xmp-general' );
+ $merger->addMetadata( array( 'fred' => 'xmp' ), 'xmp-general' );
+ $merger->addMetadata( array( 'foo' => 'iptc (hash)' ), 'iptc-good-hash' );
+ $merger->addMetadata( array( 'bar' => 'iptc (bad hash)' ), 'iptc-bad-hash' );
+ $merger->addMetadata( array( 'baz' => 'iptc (bad hash)' ), 'iptc-bad-hash' );
+ $merger->addMetadata( array( 'fred' => 'iptc (no hash)' ), 'iptc-no-hash' );
+ $merger->addMetadata( array( 'baz' => 'exif' ), 'exif' );
+
+ $actual = $merger->getMetadataArray();
+ $expected = array(
+ 'foo' => 'xmp',
+ 'bar' => 'iptc (bad hash)',
+ 'baz' => 'exif',
+ 'fred' => 'xmp',
+ );
+ $this->assertEquals( $expected, $actual );
+ }
+
+ public function testPNGXMP() {
+ $handler = new BitmapMetadataHandler();
+ $result = $handler->png( $this->filePath . 'xmp.png' );
+ $expected = array (
+ 'frameCount' => 0,
+ 'loopCount' => 1,
+ 'duration' => 0,
+ 'bitDepth' => 1,
+ 'colorType' => 'index-coloured',
+ 'metadata' => array (
+ 'SerialNumber' => '123456789',
+ '_MW_PNG_VERSION' => 1,
+ ),
+ );
+ $this->assertEquals( $expected, $result );
+ }
+ public function testPNGNative() {
+ $handler = new BitmapMetadataHandler();
+ $result = $handler->png( $this->filePath . 'Png-native-test.png' );
+ $expected = 'http://example.com/url';
+ $this->assertEquals( $expected, $result['metadata']['Identifier']['x-default'] );
+ }
+ public function testTiffByteOrder() {
+ $handler = new BitmapMetadataHandler();
+ $res = $handler->getTiffByteOrder( $this->filePath . 'test.tiff' );
+ $this->assertEquals( 'LE', $res );
+ }
+
+}
diff --git a/tests/phpunit/includes/media/BitmapScalingTest.php b/tests/phpunit/includes/media/BitmapScalingTest.php
new file mode 100644
index 00000000..5bcd3232
--- /dev/null
+++ b/tests/phpunit/includes/media/BitmapScalingTest.php
@@ -0,0 +1,136 @@
+<?php
+
+class BitmapScalingTest extends MediaWikiTestCase {
+
+ function setUp() {
+ global $wgMaxImageArea;
+ $this->oldMaxImageArea = $wgMaxImageArea;
+ $wgMaxImageArea = 1.25e7; // 3500x3500
+ }
+ function tearDown() {
+ global $wgMaxImageArea;
+ $wgMaxImageArea = $this->oldMaxImageArea;
+ }
+ /**
+ * @dataProvider provideNormaliseParams
+ */
+ function testNormaliseParams( $fileDimensions, $expectedParams, $params, $msg ) {
+ $file = new FakeDimensionFile( $fileDimensions );
+ $handler = new BitmapHandler;
+ $valid = $handler->normaliseParams( $file, $params );
+ $this->assertTrue( $valid );
+ $this->assertEquals( $expectedParams, $params, $msg );
+ }
+
+ function provideNormaliseParams() {
+ return array(
+ /* Regular resize operations */
+ array(
+ array( 1024, 768 ),
+ array(
+ 'width' => 512, 'height' => 384,
+ 'physicalWidth' => 512, 'physicalHeight' => 384,
+ 'page' => 1,
+ ),
+ array( 'width' => 512 ),
+ 'Resizing with width set',
+ ),
+ array(
+ array( 1024, 768 ),
+ array(
+ 'width' => 512, 'height' => 384,
+ 'physicalWidth' => 512, 'physicalHeight' => 384,
+ 'page' => 1,
+ ),
+ array( 'width' => 512, 'height' => 768 ),
+ 'Resizing with height set too high',
+ ),
+ array(
+ array( 1024, 768 ),
+ array(
+ 'width' => 512, 'height' => 384,
+ 'physicalWidth' => 512, 'physicalHeight' => 384,
+ 'page' => 1,
+ ),
+ array( 'width' => 1024, 'height' => 384 ),
+ 'Resizing with height set',
+ ),
+
+ /* Very tall images */
+ array(
+ array( 1000, 100 ),
+ array(
+ 'width' => 5, 'height' => 1,
+ 'physicalWidth' => 5, 'physicalHeight' => 1,
+ 'page' => 1,
+ ),
+ array( 'width' => 5 ),
+ 'Very wide image',
+ ),
+
+ array(
+ array( 100, 1000 ),
+ array(
+ 'width' => 1, 'height' => 10,
+ 'physicalWidth' => 1, 'physicalHeight' => 10,
+ 'page' => 1,
+ ),
+ array( 'width' => 1 ),
+ 'Very high image',
+ ),
+ array(
+ array( 100, 1000 ),
+ array(
+ 'width' => 1, 'height' => 5,
+ 'physicalWidth' => 1, 'physicalHeight' => 10,
+ 'page' => 1,
+ ),
+ array( 'width' => 10, 'height' => 5 ),
+ 'Very high image with height set',
+ ),
+ /* Max image area */
+ array(
+ array( 4000, 4000 ),
+ array(
+ 'width' => 5000, 'height' => 5000,
+ 'physicalWidth' => 4000, 'physicalHeight' => 4000,
+ 'page' => 1,
+ ),
+ array( 'width' => 5000 ),
+ 'Bigger than max image size but doesn\'t need scaling',
+ ),
+ );
+ }
+ function testTooBigImage() {
+ $file = new FakeDimensionFile( array( 4000, 4000 ) );
+ $handler = new BitmapHandler;
+ $params = array( 'width' => '3700' ); // Still bigger than max size.
+ $this->assertFalse( $handler->normaliseParams( $file, $params ) );
+ }
+ function testTooBigMustRenderImage() {
+ $file = new FakeDimensionFile( array( 4000, 4000 ) );
+ $file->mustRender = true;
+ $handler = new BitmapHandler;
+ $params = array( 'width' => '5000' ); // Still bigger than max size.
+ $this->assertFalse( $handler->normaliseParams( $file, $params ) );
+ }
+}
+
+class FakeDimensionFile extends File {
+ public $mustRender = false;
+
+ public function __construct( $dimensions ) {
+ parent::__construct( Title::makeTitle( NS_FILE, 'Test' ), null );
+
+ $this->dimensions = $dimensions;
+ }
+ public function getWidth( $page = 1 ) {
+ return $this->dimensions[0];
+ }
+ public function getHeight( $page = 1 ) {
+ return $this->dimensions[1];
+ }
+ public function mustRender() {
+ return $this->mustRender;
+ }
+}
diff --git a/tests/phpunit/includes/media/ExifBitmapTest.php b/tests/phpunit/includes/media/ExifBitmapTest.php
new file mode 100644
index 00000000..4282d3c8
--- /dev/null
+++ b/tests/phpunit/includes/media/ExifBitmapTest.php
@@ -0,0 +1,122 @@
+<?php
+class ExifBitmapTest extends MediaWikiTestCase {
+
+ public function setUp() {
+ global $wgShowEXIF;
+ $this->showExif = $wgShowEXIF;
+ $wgShowEXIF = true;
+ $this->handler = new ExifBitmapHandler;
+ if ( !wfDl( 'exif' ) ) {
+ $this->markTestSkipped( "This test needs the exif extension." );
+ }
+ }
+
+ public function tearDown() {
+ global $wgShowEXIF;
+ $wgShowEXIF = $this->showExif;
+ }
+
+ public function testIsOldBroken() {
+ if ( !wfDl( 'exif' ) ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+ $res = $this->handler->isMetadataValid( null, ExifBitmapHandler::OLD_BROKEN_FILE );
+ $this->assertEquals( ExifBitmapHandler::METADATA_COMPATIBLE, $res );
+ }
+ public function testIsBrokenFile() {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+ $res = $this->handler->isMetadataValid( null, ExifBitmapHandler::BROKEN_FILE );
+ $this->assertEquals( ExifBitmapHandler::METADATA_GOOD, $res );
+ }
+ public function testIsInvalid() {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+ $res = $this->handler->isMetadataValid( null, 'Something Invalid Here.' );
+ $this->assertEquals( ExifBitmapHandler::METADATA_BAD, $res );
+ }
+ public function testGoodMetadata() {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+ $meta = 'a:16:{s:10:"ImageWidth";i:20;s:11:"ImageLength";i:20;s:13:"BitsPerSample";a:3:{i:0;i:8;i:1;i:8;i:2;i:8;}s:11:"Compression";i:5;s:25:"PhotometricInterpretation";i:2;s:16:"ImageDescription";s:17:"Created with GIMP";s:12:"StripOffsets";i:8;s:11:"Orientation";i:1;s:15:"SamplesPerPixel";i:3;s:12:"RowsPerStrip";i:64;s:15:"StripByteCounts";i:238;s:11:"XResolution";s:19:"1207959552/16777216";s:11:"YResolution";s:19:"1207959552/16777216";s:19:"PlanarConfiguration";i:1;s:14:"ResolutionUnit";i:2;s:22:"MEDIAWIKI_EXIF_VERSION";i:2;}';
+ $res = $this->handler->isMetadataValid( null, $meta );
+ $this->assertEquals( ExifBitmapHandler::METADATA_GOOD, $res );
+ }
+ public function testIsOldGood() {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+ $meta = 'a:16:{s:10:"ImageWidth";i:20;s:11:"ImageLength";i:20;s:13:"BitsPerSample";a:3:{i:0;i:8;i:1;i:8;i:2;i:8;}s:11:"Compression";i:5;s:25:"PhotometricInterpretation";i:2;s:16:"ImageDescription";s:17:"Created with GIMP";s:12:"StripOffsets";i:8;s:11:"Orientation";i:1;s:15:"SamplesPerPixel";i:3;s:12:"RowsPerStrip";i:64;s:15:"StripByteCounts";i:238;s:11:"XResolution";s:19:"1207959552/16777216";s:11:"YResolution";s:19:"1207959552/16777216";s:19:"PlanarConfiguration";i:1;s:14:"ResolutionUnit";i:2;s:22:"MEDIAWIKI_EXIF_VERSION";i:1;}';
+ $res = $this->handler->isMetadataValid( null, $meta );
+ $this->assertEquals( ExifBitmapHandler::METADATA_COMPATIBLE, $res );
+ }
+ // Handle metadata from paged tiff handler (gotten via instant commons)
+ // gracefully.
+ public function testPagedTiffHandledGracefully() {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+ $meta = 'a:6:{s:9:"page_data";a:1:{i:1;a:5:{s:5:"width";i:643;s:6:"height";i:448;s:5:"alpha";s:4:"true";s:4:"page";i:1;s:6:"pixels";i:288064;}}s:10:"page_count";i:1;s:10:"first_page";i:1;s:9:"last_page";i:1;s:4:"exif";a:9:{s:10:"ImageWidth";i:643;s:11:"ImageLength";i:448;s:11:"Compression";i:5;s:25:"PhotometricInterpretation";i:2;s:11:"Orientation";i:1;s:15:"SamplesPerPixel";i:4;s:12:"RowsPerStrip";i:50;s:19:"PlanarConfiguration";i:1;s:22:"MEDIAWIKI_EXIF_VERSION";i:1;}s:21:"TIFF_METADATA_VERSION";s:3:"1.4";}';
+ $res = $this->handler->isMetadataValid( null, $meta );
+ $this->assertEquals( ExifBitmapHandler::METADATA_BAD, $res );
+ }
+
+ function testConvertMetadataLatest() {
+ $metadata = array(
+ 'foo' => array( 'First', 'Second', '_type' => 'ol' ),
+ 'MEDIAWIKI_EXIF_VERSION' => 2
+ );
+ $res = $this->handler->convertMetadataVersion( $metadata, 2 );
+ $this->assertEquals( $metadata, $res );
+ }
+ function testConvertMetadataToOld() {
+ $metadata = array(
+ 'foo' => array( 'First', 'Second', '_type' => 'ol' ),
+ 'bar' => array( 'First', 'Second', '_type' => 'ul' ),
+ 'baz' => array( 'First', 'Second' ),
+ 'fred' => 'Single',
+ 'MEDIAWIKI_EXIF_VERSION' => 2,
+ );
+ $expected = array(
+ 'foo' => "\n#First\n#Second",
+ 'bar' => "\n*First\n*Second",
+ 'baz' => "\n*First\n*Second",
+ 'fred' => 'Single',
+ 'MEDIAWIKI_EXIF_VERSION' => 1,
+ );
+ $res = $this->handler->convertMetadataVersion( $metadata, 1 );
+ $this->assertEquals( $expected, $res );
+ }
+ function testConvertMetadataSoftware() {
+ $metadata = array(
+ 'Software' => array( array('GIMP', '1.1' ) ),
+ 'MEDIAWIKI_EXIF_VERSION' => 2,
+ );
+ $expected = array(
+ 'Software' => 'GIMP (Version 1.1)',
+ 'MEDIAWIKI_EXIF_VERSION' => 1,
+ );
+ $res = $this->handler->convertMetadataVersion( $metadata, 1 );
+ $this->assertEquals( $expected, $res );
+ }
+ function testConvertMetadataSoftwareNormal() {
+ $metadata = array(
+ 'Software' => array( "GIMP 1.2", "vim" ),
+ 'MEDIAWIKI_EXIF_VERSION' => 2,
+ );
+ $expected = array(
+ 'Software' => "\n*GIMP 1.2\n*vim",
+ 'MEDIAWIKI_EXIF_VERSION' => 1,
+ );
+ $res = $this->handler->convertMetadataVersion( $metadata, 1 );
+ $this->assertEquals( $expected, $res );
+ }
+}
diff --git a/tests/phpunit/includes/media/ExifRotationTest.php b/tests/phpunit/includes/media/ExifRotationTest.php
new file mode 100644
index 00000000..639091d0
--- /dev/null
+++ b/tests/phpunit/includes/media/ExifRotationTest.php
@@ -0,0 +1,249 @@
+<?php
+
+/**
+ * Tests related to auto rotation
+ */
+class ExifRotationTest extends MediaWikiTestCase {
+
+ function setUp() {
+ parent::setUp();
+ $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ $this->handler = new BitmapHandler();
+ $this->repo = new FSRepo(array(
+ 'name' => 'temp',
+ 'directory' => wfTempDir() . '/exif-test-' . time() . '-' . mt_rand(),
+ 'url' => 'http://localhost/thumbtest'
+ ));
+ if ( !wfDl( 'exif' ) ) {
+ $this->markTestSkipped( "This test needs the exif extension." );
+ }
+ global $wgShowEXIF;
+ $this->show = $wgShowEXIF;
+ $wgShowEXIF = true;
+
+ global $wgEnableAutoRotation;
+ $this->oldAuto = $wgEnableAutoRotation;
+ $wgEnableAutoRotation = true;
+ }
+ public function tearDown() {
+ global $wgShowEXIF, $wgEnableAutoRotation;
+ $wgShowEXIF = $this->show;
+ $wgEnableAutoRotation = $this->oldAuto;
+ }
+
+ /**
+ *
+ * @dataProvider providerFiles
+ */
+ function testMetadata( $name, $type, $info ) {
+ if ( !BitmapHandler::canRotate() ) {
+ $this->markTestSkipped( "This test needs a rasterizer that can auto-rotate." );
+ }
+ $file = UnregisteredLocalFile::newFromPath( $this->filePath . $name, $type );
+ $this->assertEquals( $info['width'], $file->getWidth(), "$name: width check" );
+ $this->assertEquals( $info['height'], $file->getHeight(), "$name: height check" );
+ }
+
+ /**
+ *
+ * @dataProvider providerFiles
+ */
+ function testRotationRendering( $name, $type, $info, $thumbs ) {
+ if ( !BitmapHandler::canRotate() ) {
+ $this->markTestSkipped( "This test needs a rasterizer that can auto-rotate." );
+ }
+ foreach( $thumbs as $size => $out ) {
+ if( preg_match('/^(\d+)px$/', $size, $matches ) ) {
+ $params = array(
+ 'width' => $matches[1],
+ );
+ } elseif ( preg_match( '/^(\d+)x(\d+)px$/', $size, $matches ) ) {
+ $params = array(
+ 'width' => $matches[1],
+ 'height' => $matches[2]
+ );
+ } else {
+ throw new MWException('bogus test data format ' . $size);
+ }
+
+ $file = $this->localFile( $name, $type );
+ $thumb = $file->transform( $params, File::RENDER_NOW );
+
+ $this->assertEquals( $out[0], $thumb->getWidth(), "$name: thumb reported width check for $size" );
+ $this->assertEquals( $out[1], $thumb->getHeight(), "$name: thumb reported height check for $size" );
+
+ $gis = getimagesize( $thumb->getPath() );
+ if ($out[0] > $info['width']) {
+ // Physical image won't be scaled bigger than the original.
+ $this->assertEquals( $info['width'], $gis[0], "$name: thumb actual width check for $size");
+ $this->assertEquals( $info['height'], $gis[1], "$name: thumb actual height check for $size");
+ } else {
+ $this->assertEquals( $out[0], $gis[0], "$name: thumb actual width check for $size");
+ $this->assertEquals( $out[1], $gis[1], "$name: thumb actual height check for $size");
+ }
+ }
+ }
+
+ private function localFile( $name, $type ) {
+ return new UnregisteredLocalFile( false, $this->repo, $this->filePath . $name, $type );
+ }
+
+ function providerFiles() {
+ return array(
+ array(
+ 'landscape-plain.jpg',
+ 'image/jpeg',
+ array(
+ 'width' => 1024,
+ 'height' => 768,
+ ),
+ array(
+ '800x600px' => array( 800, 600 ),
+ '9999x800px' => array( 1067, 800 ),
+ '800px' => array( 800, 600 ),
+ '600px' => array( 600, 450 ),
+ )
+ ),
+ array(
+ 'portrait-rotated.jpg',
+ 'image/jpeg',
+ array(
+ 'width' => 768, // as rotated
+ 'height' => 1024, // as rotated
+ ),
+ array(
+ '800x600px' => array( 450, 600 ),
+ '9999x800px' => array( 600, 800 ),
+ '800px' => array( 800, 1067 ),
+ '600px' => array( 600, 800 ),
+ )
+ )
+ );
+ }
+
+ /**
+ * Same as before, but with auto-rotation disabled.
+ * @dataProvider providerFilesNoAutoRotate
+ */
+ function testMetadataNoAutoRotate( $name, $type, $info ) {
+ global $wgEnableAutoRotation;
+ $wgEnableAutoRotation = false;
+
+ $file = UnregisteredLocalFile::newFromPath( $this->filePath . $name, $type );
+ $this->assertEquals( $info['width'], $file->getWidth(), "$name: width check" );
+ $this->assertEquals( $info['height'], $file->getHeight(), "$name: height check" );
+
+ $wgEnableAutoRotation = true;
+ }
+
+ /**
+ *
+ * @dataProvider providerFilesNoAutoRotate
+ */
+ function testRotationRenderingNoAutoRotate( $name, $type, $info, $thumbs ) {
+ global $wgEnableAutoRotation;
+ $wgEnableAutoRotation = false;
+
+ foreach( $thumbs as $size => $out ) {
+ if( preg_match('/^(\d+)px$/', $size, $matches ) ) {
+ $params = array(
+ 'width' => $matches[1],
+ );
+ } elseif ( preg_match( '/^(\d+)x(\d+)px$/', $size, $matches ) ) {
+ $params = array(
+ 'width' => $matches[1],
+ 'height' => $matches[2]
+ );
+ } else {
+ throw new MWException('bogus test data format ' . $size);
+ }
+
+ $file = $this->localFile( $name, $type );
+ $thumb = $file->transform( $params, File::RENDER_NOW );
+
+ $this->assertEquals( $out[0], $thumb->getWidth(), "$name: thumb reported width check for $size" );
+ $this->assertEquals( $out[1], $thumb->getHeight(), "$name: thumb reported height check for $size" );
+
+ $gis = getimagesize( $thumb->getPath() );
+ if ($out[0] > $info['width']) {
+ // Physical image won't be scaled bigger than the original.
+ $this->assertEquals( $info['width'], $gis[0], "$name: thumb actual width check for $size");
+ $this->assertEquals( $info['height'], $gis[1], "$name: thumb actual height check for $size");
+ } else {
+ $this->assertEquals( $out[0], $gis[0], "$name: thumb actual width check for $size");
+ $this->assertEquals( $out[1], $gis[1], "$name: thumb actual height check for $size");
+ }
+ }
+ $wgEnableAutoRotation = true;
+ }
+
+ function providerFilesNoAutoRotate() {
+ return array(
+ array(
+ 'landscape-plain.jpg',
+ 'image/jpeg',
+ array(
+ 'width' => 1024,
+ 'height' => 768,
+ ),
+ array(
+ '800x600px' => array( 800, 600 ),
+ '9999x800px' => array( 1067, 800 ),
+ '800px' => array( 800, 600 ),
+ '600px' => array( 600, 450 ),
+ )
+ ),
+ array(
+ 'portrait-rotated.jpg',
+ 'image/jpeg',
+ array(
+ 'width' => 1024, // since not rotated
+ 'height' => 768, // since not rotated
+ ),
+ array(
+ '800x600px' => array( 800, 600 ),
+ '9999x800px' => array( 1067, 800 ),
+ '800px' => array( 800, 600 ),
+ '600px' => array( 600, 450 ),
+ )
+ )
+ );
+ }
+
+
+ const TEST_WIDTH = 100;
+ const TEST_HEIGHT = 200;
+
+ /**
+ * @dataProvider provideBitmapExtractPreRotationDimensions
+ */
+ function testBitmapExtractPreRotationDimensions( $rotation, $expected ) {
+ $result = $this->handler->extractPreRotationDimensions( array(
+ 'physicalWidth' => self::TEST_WIDTH,
+ 'physicalHeight' => self::TEST_HEIGHT,
+ ), $rotation );
+ $this->assertEquals( $expected, $result );
+ }
+
+ function provideBitmapExtractPreRotationDimensions() {
+ return array(
+ array(
+ 0,
+ array( self::TEST_WIDTH, self::TEST_HEIGHT )
+ ),
+ array(
+ 90,
+ array( self::TEST_HEIGHT, self::TEST_WIDTH )
+ ),
+ array(
+ 180,
+ array( self::TEST_WIDTH, self::TEST_HEIGHT )
+ ),
+ array(
+ 270,
+ array( self::TEST_HEIGHT, self::TEST_WIDTH )
+ ),
+ );
+ }
+}
+
diff --git a/tests/phpunit/includes/media/ExifTest.php b/tests/phpunit/includes/media/ExifTest.php
new file mode 100644
index 00000000..9b490e92
--- /dev/null
+++ b/tests/phpunit/includes/media/ExifTest.php
@@ -0,0 +1,51 @@
+<?php
+class ExifTest extends MediaWikiTestCase {
+
+ public function setUp() {
+ $this->mediaPath = dirname( __FILE__ ) . '/../../data/media/';
+
+ global $wgShowEXIF;
+ $this->showExif = $wgShowEXIF;
+ $wgShowEXIF = true;
+ }
+ public function tearDown() {
+ global $wgShowEXIF;
+ $wgShowEXIF = $this->showExif;
+ }
+
+ public function testGPSExtraction() {
+ if ( !wfDl( 'exif' ) ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+
+ $filename = $this->mediaPath . 'exif-gps.jpg';
+ $seg = JpegMetadataExtractor::segmentSplitter( $filename );
+ $exif = new Exif( $filename, $seg['byteOrder'] );
+ $data = $exif->getFilteredData();
+ $expected = array(
+ 'GPSLatitude' => 88.5180555556,
+ 'GPSLongitude' => -21.12357,
+ 'GPSAltitude' => -200,
+ 'GPSDOP' => '5/1',
+ 'GPSVersionID' => '2.2.0.0',
+ );
+ $this->assertEquals( $expected, $data, '', 0.0000000001 );
+ }
+ public function testUnicodeUserComment() {
+ if ( !wfDl( 'exif' ) ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+
+ $filename = $this->mediaPath . 'exif-user-comment.jpg';
+ $seg = JpegMetadataExtractor::segmentSplitter( $filename );
+ $exif = new Exif( $filename, $seg['byteOrder'] );
+ $data = $exif->getFilteredData();
+
+ $expected = array(
+ 'UserComment' => 'testâ”comment'
+ );
+ $this->assertEquals( $expected, $data );
+ }
+
+
+}
diff --git a/tests/phpunit/includes/media/FormatMetadataTest.php b/tests/phpunit/includes/media/FormatMetadataTest.php
new file mode 100644
index 00000000..db36dea3
--- /dev/null
+++ b/tests/phpunit/includes/media/FormatMetadataTest.php
@@ -0,0 +1,29 @@
+<?php
+class FormatMetadataTest extends MediaWikiTestCase {
+ public function testInvalidDate() {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+
+ $file = UnregisteredLocalFile::newFromPath( dirname( __FILE__ ) .
+ '/../../data/media/broken_exif_date.jpg', 'image/jpeg' );
+
+ // Throws an error if bug hit
+ $meta = $file->formatMetadata();
+ $this->assertNotEquals( false, $meta, 'Valid metadata extracted' );
+
+ // Find date exif entry
+ $this->assertArrayHasKey( 'visible', $meta );
+ $dateIndex = null;
+ foreach ( $meta['visible'] as $i => $data ) {
+ if ( $data['id'] == 'exif-datetimeoriginal' ) {
+ $dateIndex = $i;
+ }
+ }
+ $this->assertNotNull( $dateIndex, 'Date entry exists in metadata' );
+ $this->assertEquals( '0000:01:00 00:02:27',
+ $meta['visible'][$dateIndex]['value'],
+ 'File with invalid date metadata (bug 29471)' );
+ }
+} \ No newline at end of file
diff --git a/tests/phpunit/includes/media/GIFMetadataExtractorTest.php b/tests/phpunit/includes/media/GIFMetadataExtractorTest.php
new file mode 100644
index 00000000..59b30441
--- /dev/null
+++ b/tests/phpunit/includes/media/GIFMetadataExtractorTest.php
@@ -0,0 +1,95 @@
+<?php
+class GIFMetadataExtractorTest extends MediaWikiTestCase {
+
+ public function setUp() {
+ $this->mediaPath = dirname( __FILE__ ) . '/../../data/media/';
+ }
+ /**
+ * Put in a file, and see if the metadata coming out is as expected.
+ * @param $filename String
+ * @param $expected Array The extracted metadata.
+ * @dataProvider dataGetMetadata
+ */
+ public function testGetMetadata( $filename, $expected ) {
+ $actual = GIFMetadataExtractor::getMetadata( $this->mediaPath . $filename );
+ $this->assertEquals( $expected, $actual );
+ }
+ public function dataGetMetadata() {
+
+ $xmpNugget = <<<EOF
+<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>
+<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 7.30'>
+<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
+
+ <rdf:Description rdf:about=''
+ xmlns:Iptc4xmpCore='http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/'>
+ <Iptc4xmpCore:Location>The interwebs</Iptc4xmpCore:Location>
+ </rdf:Description>
+
+ <rdf:Description rdf:about=''
+ xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
+ <tiff:Artist>Bawolff</tiff:Artist>
+ <tiff:ImageDescription>
+ <rdf:Alt>
+ <rdf:li xml:lang='x-default'>A file to test GIF</rdf:li>
+ </rdf:Alt>
+ </tiff:ImageDescription>
+ </rdf:Description>
+</rdf:RDF>
+</x:xmpmeta>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<?xpacket end='w'?>
+EOF;
+
+ return array(
+ array( 'nonanimated.gif', array(
+ 'comment' => array( 'GIF test file â• Created with GIMP' ),
+ 'duration' => 0.1,
+ 'frameCount' => 1,
+ 'looped' => false,
+ 'xmp' => '',
+ )
+ ),
+ array( 'animated.gif', array(
+ 'comment' => array( 'GIF test file . Created with GIMP' ),
+ 'duration' => 2.4,
+ 'frameCount' => 4,
+ 'looped' => true,
+ 'xmp' => '',
+ )
+ ),
+
+ array( 'animated-xmp.gif', array(
+ 'xmp' => $xmpNugget,
+ 'duration' => 2.4,
+ 'frameCount' => 4,
+ 'looped' => true,
+ 'comment' => array( 'GIƒ·test·file' ),
+ )
+ ),
+ );
+ }
+}
diff --git a/tests/phpunit/includes/media/GIFTest.php b/tests/phpunit/includes/media/GIFTest.php
new file mode 100644
index 00000000..42c25ca5
--- /dev/null
+++ b/tests/phpunit/includes/media/GIFTest.php
@@ -0,0 +1,85 @@
+<?php
+class GIFHandlerTest extends MediaWikiTestCase {
+
+ public function setUp() {
+ $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ $this->handler = new GIFHandler();
+ }
+
+ public function testInvalidFile() {
+ $res = $this->handler->getMetadata( null, $this->filePath . 'README' );
+ $this->assertEquals( GIFHandler::BROKEN_FILE, $res );
+ }
+ /**
+ * @param $filename String basename of the file to check
+ * @param $expected boolean Expected result.
+ * @dataProvider dataIsAnimated
+ */
+ public function testIsAnimanted( $filename, $expected ) {
+ $file = UnregisteredLocalFile::newFromPath( $this->filePath . $filename,
+ 'image/gif' );
+ $actual = $this->handler->isAnimatedImage( $file );
+ $this->assertEquals( $expected, $actual );
+ }
+ public function dataIsAnimated() {
+ return array(
+ array( 'animated.gif', true ),
+ array( 'nonanimated.gif', false ),
+ );
+ }
+
+ /**
+ * @param $filename String
+ * @param $expected Integer Total image area
+ * @dataProvider dataGetImageArea
+ */
+ public function testGetImageArea( $filename, $expected ) {
+ $file = UnregisteredLocalFile::newFromPath( $this->filePath . $filename,
+ 'image/gif' );
+ $actual = $this->handler->getImageArea( $file, $file->getWidth(), $file->getHeight() );
+ $this->assertEquals( $expected, $actual );
+ }
+ public function dataGetImageArea() {
+ return array(
+ array( 'animated.gif', 5400 ),
+ array( 'nonanimated.gif', 1350 ),
+ );
+ }
+
+ /**
+ * @param $metadata String Serialized metadata
+ * @param $expected Integer One of the class constants of GIFHandler
+ * @dataProvider dataIsMetadataValid
+ */
+ public function testIsMetadataValid( $metadata, $expected ) {
+ $actual = $this->handler->isMetadataValid( null, $metadata );
+ $this->assertEquals( $expected, $actual );
+ }
+ public function dataIsMetadataValid() {
+ return array(
+ array( GIFHandler::BROKEN_FILE, GIFHandler::METADATA_GOOD ),
+ array( '', GIFHandler::METADATA_BAD ),
+ array( null, GIFHandler::METADATA_BAD ),
+ array( 'Something invalid!', GIFHandler::METADATA_BAD ),
+ array( 'a:4:{s:10:"frameCount";i:1;s:6:"looped";b:0;s:8:"duration";d:0.1000000000000000055511151231257827021181583404541015625;s:8:"metadata";a:2:{s:14:"GIFFileComment";a:1:{i:0;s:35:"GIF test file â• Created with GIMP";}s:15:"_MW_GIF_VERSION";i:1;}}', GIFHandler::METADATA_GOOD ),
+ );
+ }
+
+ /**
+ * @param $filename String
+ * @param $expected String Serialized array
+ * @dataProvider dataGetMetadata
+ */
+ public function testGetMetadata( $filename, $expected ) {
+ $file = UnregisteredLocalFile::newFromPath( $this->filePath . $filename,
+ 'image/gif' );
+ $actual = $this->handler->getMetadata( $file, $this->filePath . $filename );
+ $this->assertEquals( unserialize( $expected ), unserialize( $actual ) );
+ }
+ public function dataGetMetadata() {
+ return array(
+ array( 'nonanimated.gif', 'a:4:{s:10:"frameCount";i:1;s:6:"looped";b:0;s:8:"duration";d:0.1000000000000000055511151231257827021181583404541015625;s:8:"metadata";a:2:{s:14:"GIFFileComment";a:1:{i:0;s:35:"GIF test file â• Created with GIMP";}s:15:"_MW_GIF_VERSION";i:1;}}' ),
+ array( 'animated-xmp.gif', 'a:4:{s:10:"frameCount";i:4;s:6:"looped";b:1;s:8:"duration";d:2.399999999999999911182158029987476766109466552734375;s:8:"metadata";a:5:{s:6:"Artist";s:7:"Bawolff";s:16:"ImageDescription";a:2:{s:9:"x-default";s:18:"A file to test GIF";s:5:"_type";s:4:"lang";}s:15:"SublocationDest";s:13:"The interwebs";s:14:"GIFFileComment";a:1:{i:0;s:16:"GIƒ·test·file";}s:15:"_MW_GIF_VERSION";i:1;}}' ),
+ );
+ }
+}
diff --git a/tests/phpunit/includes/media/IPTCTest.php b/tests/phpunit/includes/media/IPTCTest.php
new file mode 100644
index 00000000..ec6deeb8
--- /dev/null
+++ b/tests/phpunit/includes/media/IPTCTest.php
@@ -0,0 +1,55 @@
+<?php
+class IPTCTest extends MediaWikiTestCase {
+ public function testRecognizeUtf8() {
+ // utf-8 is the only one used in practise.
+ $res = IPTC::getCharset( "\x1b%G" );
+ $this->assertEquals( 'UTF-8', $res );
+ }
+
+ public function testIPTCParseNoCharset88591() {
+ // basically IPTC for keyword with value of 0xBC which is 1/4 in iso-8859-1
+ // This data doesn't specify a charset. We're supposed to guess
+ // (which basically means utf-8 if valid, windows 1252 (iso 8859-1) if not)
+ $iptcData = "Photoshop 3.0\08BIM\4\4\0\0\0\0\0\x06\x1c\x02\x19\x00\x01\xBC";
+ $res = IPTC::Parse( $iptcData );
+ $this->assertEquals( array( '¼' ), $res['Keywords'] );
+ }
+ /* This one contains a sequence that's valid iso 8859-1 but not valid utf8 */
+ /* \xC3 = Ã, \xB8 = ¸ */
+ public function testIPTCParseNoCharset88591b() {
+ $iptcData = "Photoshop 3.0\08BIM\4\4\0\0\0\0\0\x09\x1c\x02\x19\x00\x04\xC3\xC3\xC3\xB8";
+ $res = IPTC::Parse( $iptcData );
+ $this->assertEquals( array( 'ÃÃø' ), $res['Keywords'] );
+ }
+ /* Same as testIPTCParseNoCharset88591b, but forcing the charset to utf-8.
+ * What should happen is the first "\xC3\xC3" should be dropped as invalid,
+ * leaving \xC3\xB8, which is ø
+ */
+ public function testIPTCParseForcedUTFButInvalid() {
+ $iptcData = "Photoshop 3.0\08BIM\4\4\0\0\0\0\0\x11\x1c\x02\x19\x00\x04\xC3\xC3\xC3\xB8"
+ . "\x1c\x01\x5A\x00\x03\x1B\x25\x47";
+ $res = IPTC::Parse( $iptcData );
+ $this->assertEquals( array( 'ø' ), $res['Keywords'] );
+ }
+ public function testIPTCParseNoCharsetUTF8() {
+ $iptcData = "Photoshop 3.0\08BIM\4\4\0\0\0\0\0\x07\x1c\x02\x19\x00\x02¼";
+ $res = IPTC::Parse( $iptcData );
+ $this->assertEquals( array( '¼' ), $res['Keywords'] );
+ }
+ // Testing something that has 2 values for keyword
+ public function testIPTCParseMulti() {
+ $iptcData = /* identifier */ "Photoshop 3.0\08BIM\4\4"
+ /* length */ . "\0\0\0\0\0\x0D"
+ . "\x1c\x02\x19" . "\x00\x01" . "\xBC"
+ . "\x1c\x02\x19" . "\x00\x02" . "\xBC\xBD";
+ $res = IPTC::Parse( $iptcData );
+ $this->assertEquals( array( '¼', '¼½' ), $res['Keywords'] );
+ }
+ public function testIPTCParseUTF8() {
+ // This has the magic "\x1c\x01\x5A\x00\x03\x1B\x25\x47" which marks content as UTF8.
+ $iptcData = "Photoshop 3.0\08BIM\4\4\0\0\0\0\0\x0F\x1c\x02\x19\x00\x02¼\x1c\x01\x5A\x00\x03\x1B\x25\x47";
+ $res = IPTC::Parse( $iptcData );
+ $this->assertEquals( array( '¼' ), $res['Keywords'] );
+ }
+
+}
diff --git a/tests/phpunit/includes/media/JpegMetadataExtractorTest.php b/tests/phpunit/includes/media/JpegMetadataExtractorTest.php
new file mode 100644
index 00000000..61fc9c81
--- /dev/null
+++ b/tests/phpunit/includes/media/JpegMetadataExtractorTest.php
@@ -0,0 +1,94 @@
+<?php
+/*
+ * @todo Could use a test of extended XMP segments. Hard to find programs that
+ * create example files, and creating my own in vim propbably wouldn't
+ * serve as a very good "test". (Adobe photoshop probably creates such files
+ * but it costs money). The implementation of it currently in MediaWiki is based
+ * solely on reading the standard, without any real world test files.
+ */
+class JpegMetadataExtractorTest extends MediaWikiTestCase {
+
+ public function setUp() {
+ $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ }
+
+ /**
+ * We also use this test to test padding bytes don't
+ * screw stuff up
+ *
+ * @param $file filename
+ *
+ * @dataProvider dataUtf8Comment
+ */
+ public function testUtf8Comment( $file ) {
+ $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . $file );
+ $this->assertEquals( array( 'UTF-8 JPEG Comment — ¼' ), $res['COM'] );
+ }
+ public function dataUtf8Comment() {
+ return array(
+ array( 'jpeg-comment-utf.jpg' ),
+ array( 'jpeg-padding-even.jpg' ),
+ array( 'jpeg-padding-odd.jpg' ),
+ );
+ }
+ /** The file is iso-8859-1, but it should get auto converted */
+ public function testIso88591Comment() {
+ $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-comment-iso8859-1.jpg' );
+ $this->assertEquals( array( 'ISO-8859-1 JPEG Comment - ¼' ), $res['COM'] );
+ }
+ /** Comment values that are non-textual (random binary junk) should not be shown.
+ * The example test file has a comment with a 0x5 byte in it which is a control character
+ * and considered binary junk for our purposes.
+ */
+ public function testBinaryCommentStripped() {
+ $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-comment-binary.jpg' );
+ $this->assertEmpty( $res['COM'] );
+ }
+ /* Very rarely a file can have multiple comments.
+ * Order of comments is based on order inside the file.
+ */
+ public function testMultipleComment() {
+ $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-comment-multiple.jpg' );
+ $this->assertEquals( array( 'foo', 'bar' ), $res['COM'] );
+ }
+ public function testXMPExtraction() {
+ $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-xmp-psir.jpg' );
+ $expected = file_get_contents( $this->filePath . 'jpeg-xmp-psir.xmp' );
+ $this->assertEquals( $expected, $res['XMP'] );
+ }
+ public function testPSIRExtraction() {
+ $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-xmp-psir.jpg' );
+ $expected = '50686f746f73686f7020332e30003842494d04040000000000181c02190004746573741c02190003666f6f1c020000020004';
+ $this->assertEquals( $expected, bin2hex( $res['PSIR'] ) );
+ }
+ public function testXMPExtractionAltAppId() {
+ $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-xmp-alt.jpg' );
+ $expected = file_get_contents( $this->filePath . 'jpeg-xmp-psir.xmp' );
+ $this->assertEquals( $expected, $res['XMP'] );
+ }
+
+
+ public function testIPTCHashComparisionNoHash() {
+ $segments = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-xmp-psir.jpg' );
+ $res = JpegMetadataExtractor::doPSIR( $segments['PSIR'] );
+
+ $this->assertEquals( 'iptc-no-hash', $res );
+ }
+ public function testIPTCHashComparisionBadHash() {
+ $segments = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-iptc-bad-hash.jpg' );
+ $res = JpegMetadataExtractor::doPSIR( $segments['PSIR'] );
+
+ $this->assertEquals( 'iptc-bad-hash', $res );
+ }
+ public function testIPTCHashComparisionGoodHash() {
+ $segments = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-iptc-good-hash.jpg' );
+ $res = JpegMetadataExtractor::doPSIR( $segments['PSIR'] );
+
+ $this->assertEquals( 'iptc-good-hash', $res );
+ }
+ public function testExifByteOrder() {
+ $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'exif-user-comment.jpg' );
+ $expected = 'BE';
+ $this->assertEquals( $expected, $res['byteOrder'] );
+ }
+}
diff --git a/tests/phpunit/includes/media/JpegTest.php b/tests/phpunit/includes/media/JpegTest.php
new file mode 100644
index 00000000..713a3410
--- /dev/null
+++ b/tests/phpunit/includes/media/JpegTest.php
@@ -0,0 +1,29 @@
+<?php
+class JpegTest extends MediaWikiTestCase {
+
+ public function setUp() {
+ $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ }
+
+ public function testInvalidFile() {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+ $jpeg = new JpegHandler;
+ $res = $jpeg->getMetadata( null, $this->filePath . 'README' );
+ $this->assertEquals( ExifBitmapHandler::BROKEN_FILE, $res );
+ }
+ public function testJpegMetadataExtraction() {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+ $h = new JpegHandler;
+ $res = $h->getMetadata( null, $this->filePath . 'test.jpg' );
+ $expected = 'a:7:{s:16:"ImageDescription";s:9:"Test file";s:11:"XResolution";s:4:"72/1";s:11:"YResolution";s:4:"72/1";s:14:"ResolutionUnit";i:2;s:16:"YCbCrPositioning";i:1;s:15:"JPEGFileComment";a:1:{i:0;s:17:"Created with GIMP";}s:22:"MEDIAWIKI_EXIF_VERSION";i:2;}';
+
+ // Unserialize in case serialization format ever changes.
+ $this->assertEquals( unserialize( $expected ), unserialize( $res ) );
+ }
+}
diff --git a/tests/phpunit/includes/media/PNGMetadataExtractorTest.php b/tests/phpunit/includes/media/PNGMetadataExtractorTest.php
new file mode 100644
index 00000000..9f702c50
--- /dev/null
+++ b/tests/phpunit/includes/media/PNGMetadataExtractorTest.php
@@ -0,0 +1,141 @@
+<?php
+class PNGMetadataExtractorTest extends MediaWikiTestCase {
+
+ function setUp() {
+ $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ }
+ /**
+ * Tests zTXt tag (compressed textual metadata)
+ */
+ function testPngNativetZtxt() {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ 'Png-native-test.png' );
+ $expected = "foo bar baz foo foo foo foof foo foo foo foo";
+ $this->assertArrayHasKey( 'text', $meta );
+ $meta = $meta['text'];
+ $this->assertArrayHasKey( 'Make', $meta );
+ $this->assertArrayHasKey( 'x-default', $meta['Make'] );
+
+ $this->assertEquals( $expected, $meta['Make']['x-default'] );
+ }
+
+ /**
+ * Test tEXt tag (Uncompressed textual metadata)
+ */
+ function testPngNativeText() {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ 'Png-native-test.png' );
+ $expected = "Some long image desc";
+ $this->assertArrayHasKey( 'text', $meta );
+ $meta = $meta['text'];
+ $this->assertArrayHasKey( 'ImageDescription', $meta );
+ $this->assertArrayHasKey( 'x-default', $meta['ImageDescription'] );
+ $this->assertArrayHasKey( '_type', $meta['ImageDescription'] );
+
+ $this->assertEquals( $expected, $meta['ImageDescription']['x-default'] );
+ }
+
+ /**
+ * tEXt tags must be encoded iso-8859-1 (vs iTXt which are utf-8)
+ * Make sure non-ascii characters get converted properly
+ */
+ function testPngNativeTextNonAscii() {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ 'Png-native-test.png' );
+
+ // Note the Copyright symbol here is a utf-8 one
+ // (aka \xC2\xA9) where in the file its iso-8859-1
+ // encoded as just \xA9.
+ $expected = "© 2010 Bawolff";
+
+
+ $this->assertArrayHasKey( 'text', $meta );
+ $meta = $meta['text'];
+ $this->assertArrayHasKey( 'Copyright', $meta );
+ $this->assertArrayHasKey( 'x-default', $meta['Copyright'] );
+
+ $this->assertEquals( $expected, $meta['Copyright']['x-default'] );
+ }
+
+ /**
+ * Test extraction of pHYs tags, which can tell what the
+ * actual resolution of the image is (aka in dots per meter).
+ function testPngPhysTag () {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ 'Png-native-test.png' );
+
+ $this->assertArrayHasKey( 'text', $meta );
+ $meta = $meta['text'];
+
+ $this->assertEquals( '2835/100', $meta['XResolution'] );
+ $this->assertEquals( '2835/100', $meta['YResolution'] );
+ $this->assertEquals( 3, $meta['ResolutionUnit'] ); // 3 = cm
+ }
+
+ /**
+ * Given a normal static PNG, check the animation metadata returned.
+ */
+ function testStaticPngAnimationMetadata() {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ 'Png-native-test.png' );
+
+ $this->assertEquals( 0, $meta['frameCount'] );
+ $this->assertEquals( 1, $meta['loopCount'] );
+ $this->assertEquals( 0, $meta['duration'] );
+ }
+
+ /**
+ * Given an animated APNG image file
+ * check it gets animated metadata right.
+ */
+ function testApngAnimationMetadata() {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ 'Animated_PNG_example_bouncing_beach_ball.png' );
+
+ $this->assertEquals( 20, $meta['frameCount'] );
+ // Note loop count of 0 = infinity
+ $this->assertEquals( 0, $meta['loopCount'] );
+ $this->assertEquals( 1.5, $meta['duration'], '', 0.00001 );
+ }
+
+ function testPngBitDepth8() {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ 'Png-native-test.png' );
+
+ $this->assertEquals( 8, $meta['bitDepth'] );
+ }
+ function testPngBitDepth1() {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ '1bit-png.png' );
+ $this->assertEquals( 1, $meta['bitDepth'] );
+ }
+
+
+ function testPngIndexColour() {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ 'Png-native-test.png' );
+
+ $this->assertEquals( 'index-coloured', $meta['colorType'] );
+ }
+ function testPngRgbColour() {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ 'rgb-png.png' );
+ $this->assertEquals( 'truecolour-alpha', $meta['colorType'] );
+ }
+ function testPngRgbNoAlphaColour() {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ 'rgb-na-png.png' );
+ $this->assertEquals( 'truecolour', $meta['colorType'] );
+ }
+ function testPngGreyscaleColour() {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ 'greyscale-png.png' );
+ $this->assertEquals( 'greyscale-alpha', $meta['colorType'] );
+ }
+ function testPngGreyscaleNoAlphaColour() {
+ $meta = PNGMetadataExtractor::getMetadata( $this->filePath .
+ 'greyscale-na-png.png' );
+ $this->assertEquals( 'greyscale', $meta['colorType'] );
+ }
+
+}
diff --git a/tests/phpunit/includes/media/PNGTest.php b/tests/phpunit/includes/media/PNGTest.php
new file mode 100644
index 00000000..b782918c
--- /dev/null
+++ b/tests/phpunit/includes/media/PNGTest.php
@@ -0,0 +1,88 @@
+<?php
+class PNGHandlerTest extends MediaWikiTestCase {
+
+ public function setUp() {
+ $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ $this->handler = new PNGHandler();
+ }
+
+ public function testInvalidFile() {
+ $res = $this->handler->getMetadata( null, $this->filePath . 'README' );
+ $this->assertEquals( PNGHandler::BROKEN_FILE, $res );
+ }
+ /**
+ * @param $filename String basename of the file to check
+ * @param $expected boolean Expected result.
+ * @dataProvider dataIsAnimated
+ */
+ public function testIsAnimanted( $filename, $expected ) {
+ $file = UnregisteredLocalFile::newFromPath( $this->filePath . $filename,
+ 'image/png' );
+ $actual = $this->handler->isAnimatedImage( $file );
+ $this->assertEquals( $expected, $actual );
+ }
+ public function dataIsAnimated() {
+ return array(
+ array( 'Animated_PNG_example_bouncing_beach_ball.png', true ),
+ array( '1bit-png.png', false ),
+ );
+ }
+
+ /**
+ * @param $filename String
+ * @param $expected Integer Total image area
+ * @dataProvider dataGetImageArea
+ */
+ public function testGetImageArea( $filename, $expected ) {
+ $file = UnregisteredLocalFile::newFromPath( $this->filePath . $filename,
+ 'image/png' );
+ $actual = $this->handler->getImageArea( $file, $file->getWidth(), $file->getHeight() );
+ $this->assertEquals( $expected, $actual );
+ }
+ public function dataGetImageArea() {
+ return array(
+ array( '1bit-png.png', 2500 ),
+ array( 'greyscale-png.png', 2500 ),
+ array( 'Png-native-test.png', 126000 ),
+ array( 'Animated_PNG_example_bouncing_beach_ball.png', 10000 ),
+ );
+ }
+
+ /**
+ * @param $metadata String Serialized metadata
+ * @param $expected Integer One of the class constants of PNGHandler
+ * @dataProvider dataIsMetadataValid
+ */
+ public function testIsMetadataValid( $metadata, $expected ) {
+ $actual = $this->handler->isMetadataValid( null, $metadata );
+ $this->assertEquals( $expected, $actual );
+ }
+ public function dataIsMetadataValid() {
+ return array(
+ array( PNGHandler::BROKEN_FILE, PNGHandler::METADATA_GOOD ),
+ array( '', PNGHandler::METADATA_BAD ),
+ array( null, PNGHandler::METADATA_BAD ),
+ array( 'Something invalid!', PNGHandler::METADATA_BAD ),
+ array( 'a:6:{s:10:"frameCount";i:0;s:9:"loopCount";i:1;s:8:"duration";d:0;s:8:"bitDepth";i:8;s:9:"colorType";s:10:"truecolour";s:8:"metadata";a:1:{s:15:"_MW_PNG_VERSION";i:1;}}', PNGHandler::METADATA_GOOD ),
+ );
+ }
+
+ /**
+ * @param $filename String
+ * @param $expected String Serialized array
+ * @dataProvider dataGetMetadata
+ */
+ public function testGetMetadata( $filename, $expected ) {
+ $file = UnregisteredLocalFile::newFromPath( $this->filePath . $filename,
+ 'image/png' );
+ $actual = $this->handler->getMetadata( $file, $this->filePath . $filename );
+// $this->assertEquals( unserialize( $expected ), unserialize( $actual ) );
+ $this->assertEquals( ( $expected ), ( $actual ) );
+ }
+ public function dataGetMetadata() {
+ return array(
+ array( 'rgb-na-png.png', 'a:6:{s:10:"frameCount";i:0;s:9:"loopCount";i:1;s:8:"duration";d:0;s:8:"bitDepth";i:8;s:9:"colorType";s:10:"truecolour";s:8:"metadata";a:1:{s:15:"_MW_PNG_VERSION";i:1;}}' ),
+ array( 'xmp.png', 'a:6:{s:10:"frameCount";i:0;s:9:"loopCount";i:1;s:8:"duration";d:0;s:8:"bitDepth";i:1;s:9:"colorType";s:14:"index-coloured";s:8:"metadata";a:2:{s:12:"SerialNumber";s:9:"123456789";s:15:"_MW_PNG_VERSION";i:1;}}' ),
+ );
+ }
+}
diff --git a/tests/phpunit/includes/media/SVGMetadataExtractorTest.php b/tests/phpunit/includes/media/SVGMetadataExtractorTest.php
new file mode 100644
index 00000000..c2c81b98
--- /dev/null
+++ b/tests/phpunit/includes/media/SVGMetadataExtractorTest.php
@@ -0,0 +1,88 @@
+<?php
+
+class SVGMetadataExtractorTest extends MediaWikiTestCase {
+
+ function setUp() {
+ AutoLoader::loadClass( 'SVGMetadataExtractorTest' );
+ }
+
+ /**
+ * @dataProvider providerSvgFiles
+ */
+ function testGetMetadata( $infile, $expected ) {
+ $this->assertMetadata( $infile, $expected );
+ }
+
+ /**
+ * @dataProvider providerSvgFilesWithXMLMetadata
+ */
+ function testGetXMLMetadata( $infile, $expected ) {
+ $r = new XMLReader();
+ if( !method_exists( $r, 'readInnerXML' ) ) {
+ $this->markTestSkipped( 'XMLReader::readInnerXML() does not exist (libxml >2.6.20 needed).' );
+ return;
+ }
+ $this->assertMetadata( $infile, $expected );
+ }
+
+ function assertMetadata( $infile, $expected ) {
+ try {
+ $data = SVGMetadataExtractor::getMetadata( $infile );
+ $this->assertEquals( $expected, $data, 'SVG metadata extraction test' );
+ } catch ( MWException $e ) {
+ if ( $expected === false ) {
+ $this->assertTrue( true, 'SVG metadata extracted test (expected failure)' );
+ } else {
+ throw $e;
+ }
+ }
+ }
+
+ function providerSvgFiles() {
+ $base = dirname( __FILE__ ) . '/../../data/media';
+ return array(
+ array(
+ "$base/Wikimedia-logo.svg",
+ array(
+ 'width' => 1024,
+ 'height' => 1024
+ )
+ ),
+ array(
+ "$base/QA_icon.svg",
+ array(
+ 'width' => 60,
+ 'height' => 60
+ )
+ ),
+ array(
+ "$base/Gtk-media-play-ltr.svg",
+ array(
+ 'width' => 60,
+ 'height' => 60
+ )
+ ),
+ );
+ }
+
+ function providerSvgFilesWithXMLMetadata() {
+ $base = dirname( __FILE__ ) . '/../../data/media';
+ return array(
+ array(
+ "$base/US_states_by_total_state_tax_revenue.svg",
+ array(
+ 'height' => 593,
+ 'metadata' =>
+ '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <ns4:Work xmlns:ns4="http://creativecommons.org/ns#" rdf:about="">
+ <ns5:format xmlns:ns5="http://purl.org/dc/elements/1.1/">image/svg+xml</ns5:format>
+ <ns5:type xmlns:ns5="http://purl.org/dc/elements/1.1/" rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ </ns4:Work>
+ </rdf:RDF>',
+ 'width' => 959
+ )
+ ),
+ );
+ }
+}
+
diff --git a/tests/phpunit/includes/media/TiffTest.php b/tests/phpunit/includes/media/TiffTest.php
new file mode 100644
index 00000000..0a7e8e8c
--- /dev/null
+++ b/tests/phpunit/includes/media/TiffTest.php
@@ -0,0 +1,36 @@
+<?php
+class TiffTest extends MediaWikiTestCase {
+
+ public function setUp() {
+ global $wgShowEXIF;
+ $this->showExif = $wgShowEXIF;
+ $wgShowEXIF = true;
+ $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
+ $this->handler = new TiffHandler;
+ }
+
+ public function tearDown() {
+ global $wgShowEXIF;
+ $wgShowEXIF = $this->showExif;
+ }
+
+ public function testInvalidFile() {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+ $res = $this->handler->getMetadata( null, $this->filePath . 'README' );
+ $this->assertEquals( ExifBitmapHandler::BROKEN_FILE, $res );
+ }
+ public function testTiffMetadataExtraction() {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ $this->markTestIncomplete( "This test needs the exif extension." );
+ }
+ $res = $this->handler->getMetadata( null, $this->filePath . 'test.tiff' );
+ $expected = 'a:16:{s:10:"ImageWidth";i:20;s:11:"ImageLength";i:20;s:13:"BitsPerSample";a:3:{i:0;i:8;i:1;i:8;i:2;i:8;}s:11:"Compression";i:5;s:25:"PhotometricInterpretation";i:2;s:16:"ImageDescription";s:17:"Created with GIMP";s:12:"StripOffsets";i:8;s:11:"Orientation";i:1;s:15:"SamplesPerPixel";i:3;s:12:"RowsPerStrip";i:64;s:15:"StripByteCounts";i:238;s:11:"XResolution";s:19:"1207959552/16777216";s:11:"YResolution";s:19:"1207959552/16777216";s:19:"PlanarConfiguration";i:1;s:14:"ResolutionUnit";i:2;s:22:"MEDIAWIKI_EXIF_VERSION";i:2;}';
+ // Re-unserialize in case there are subtle differences between how versions
+ // of php serialize stuff.
+ $this->assertEquals( unserialize( $expected ), unserialize( $res ) );
+ }
+}
diff --git a/tests/phpunit/includes/media/XMPTest.php b/tests/phpunit/includes/media/XMPTest.php
new file mode 100644
index 00000000..d1ec4767
--- /dev/null
+++ b/tests/phpunit/includes/media/XMPTest.php
@@ -0,0 +1,154 @@
+<?php
+class XMPTest extends MediaWikiTestCase {
+
+ /**
+ * Put XMP in, compare what comes out...
+ *
+ * @param $xmp String the actual xml data.
+ * @param $expected Array expected result of parsing the xmp.
+ * @param $info String Short sentence on what's being tested.
+ *
+ * @dataProvider dataXMPParse
+ */
+ public function testXMPParse( $xmp, $expected, $info ) {
+ if ( !function_exists( 'xml_parser_create_ns' ) ) {
+ $this->markIncomplete( 'Requires libxml to do XMP parsing' );
+ }
+ if ( !is_string( $xmp ) || !is_array( $expected ) ) {
+ throw new Exception( "Invalid data provided to " . __METHOD__ );
+ }
+ $reader = new XMPReader;
+ $reader->parse( $xmp );
+ $this->assertEquals( $expected, $reader->getResults(), $info );
+ }
+
+ public function dataXMPParse() {
+ $xmpPath = dirname( __FILE__ ) . '/../../data/xmp/' ;
+ $data = array();
+
+ // $xmpFiles format: array of arrays with first arg file base name,
+ // with the actual file having .xmp on the end for the xmp
+ // and .result.php on the end for a php file containing the result
+ // array. Second argument is some info on what's being tested.
+ $xmpFiles = array(
+ array( '1', 'parseType=Resource test' ),
+ array( '2', 'Structure with mixed attribute and element props' ),
+ array( '3', 'Extra qualifiers (that should be ignored)' ),
+ array( '3-invalid', 'Test ignoring qualifiers that look like normal props' ),
+ array( '4', 'Flash as qualifier' ),
+ array( '5', 'Flash as qualifier 2' ),
+ array( '6', 'Multiple rdf:Description' ),
+ array( '7', 'Generic test of several property types' ),
+ array( 'flash', 'Test of Flash property' ),
+ array( 'invalid-child-not-struct', 'Test child props not in struct or ignored' ),
+ array( 'no-recognized-props', 'Test namespace and no recognized props' ),
+ array( 'no-namespace', 'Test non-namespaced attributes are ignored' ),
+ array( 'bag-for-seq', "Allow bag's instead of seq's. (bug 27105)" ),
+ array( 'utf16BE', 'UTF-16BE encoding' ),
+ array( 'utf16LE', 'UTF-16LE encoding' ),
+ array( 'utf32BE', 'UTF-32BE encoding' ),
+ array( 'utf32LE', 'UTF-32LE encoding' ),
+ array( 'xmpExt', 'Extended XMP missing second part' ),
+ );
+ foreach( $xmpFiles as $file ) {
+ $xmp = file_get_contents( $xmpPath . $file[0] . '.xmp' );
+ // I'm not sure if this is the best way to handle getting the
+ // result array, but it seems kind of big to put directly in the test
+ // file.
+ $result = null;
+ include( $xmpPath . $file[0] . '.result.php' );
+ $data[] = array( $xmp, $result, '[' . $file[0] . '.xmp] ' . $file[1] );
+ }
+ return $data;
+ }
+
+ /** Test ExtendedXMP block support. (Used when the XMP has to be split
+ * over multiple jpeg segments, due to 64k size limit on jpeg segments.
+ *
+ * @todo This is based on what the standard says. Need to find a real
+ * world example file to double check the support for this is right.
+ */
+ function testExtendedXMP() {
+ $xmpPath = dirname( __FILE__ ) . '/../../data/xmp/';
+ $standardXMP = file_get_contents( $xmpPath . 'xmpExt.xmp' );
+ $extendedXMP = file_get_contents( $xmpPath . 'xmpExt2.xmp' );
+
+ $md5sum = '28C74E0AC2D796886759006FBE2E57B7'; // of xmpExt2.xmp
+ $length = pack( 'N', strlen( $extendedXMP ) );
+ $offset = pack( 'N', 0 );
+ $extendedPacket = $md5sum . $length . $offset . $extendedXMP;
+
+ $reader = new XMPReader();
+ $reader->parse( $standardXMP );
+ $reader->parseExtended( $extendedPacket );
+ $actual = $reader->getResults();
+
+ $expected = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ 'Flash' => 9,
+ 'FNumber' => '2/10',
+ )
+ );
+
+ $this->assertEquals( $expected, $actual );
+ }
+
+ /**
+ * This test has an extended XMP block with a wrong guid (md5sum)
+ * and thus should only return the StandardXMP, not the ExtendedXMP.
+ */
+ function testExtendedXMPWithWrongGUID() {
+ $xmpPath = dirname( __FILE__ ) . '/../../data/xmp/';
+ $standardXMP = file_get_contents( $xmpPath . 'xmpExt.xmp' );
+ $extendedXMP = file_get_contents( $xmpPath . 'xmpExt2.xmp' );
+
+ $md5sum = '28C74E0AC2D796886759006FBE2E57B9'; // Note last digit.
+ $length = pack( 'N', strlen( $extendedXMP ) );
+ $offset = pack( 'N', 0 );
+ $extendedPacket = $md5sum . $length . $offset . $extendedXMP;
+
+ $reader = new XMPReader();
+ $reader->parse( $standardXMP );
+ $reader->parseExtended( $extendedPacket );
+ $actual = $reader->getResults();
+
+ $expected = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ 'Flash' => 9,
+ )
+ );
+
+ $this->assertEquals( $expected, $actual );
+ }
+ /**
+ * Have a high offset to simulate a missing packet,
+ * which should cause it to ignore the ExtendedXMP packet.
+ */
+ function testExtendedXMPMissingPacket() {
+ $xmpPath = dirname( __FILE__ ) . '/../../data/xmp/';
+ $standardXMP = file_get_contents( $xmpPath . 'xmpExt.xmp' );
+ $extendedXMP = file_get_contents( $xmpPath . 'xmpExt2.xmp' );
+
+ $md5sum = '28C74E0AC2D796886759006FBE2E57B7'; // of xmpExt2.xmp
+ $length = pack( 'N', strlen( $extendedXMP ) );
+ $offset = pack( 'N', 2048 );
+ $extendedPacket = $md5sum . $length . $offset . $extendedXMP;
+
+ $reader = new XMPReader();
+ $reader->parse( $standardXMP );
+ $reader->parseExtended( $extendedPacket );
+ $actual = $reader->getResults();
+
+ $expected = array( 'xmp-exif' =>
+ array(
+ 'DigitalZoomRatio' => '0/10',
+ 'Flash' => 9,
+ )
+ );
+
+ $this->assertEquals( $expected, $actual );
+ }
+
+}
diff --git a/tests/phpunit/includes/normal/CleanUpTest.php b/tests/phpunit/includes/normal/CleanUpTest.php
new file mode 100644
index 00000000..d5ad18d8
--- /dev/null
+++ b/tests/phpunit/includes/normal/CleanUpTest.php
@@ -0,0 +1,382 @@
+<?php
+/**
+ * Tests for UtfNormal::cleanUp() function.
+ *
+ * Copyright © 2004 Brion Vibber <brion@pobox.com>
+ * http://www.mediawiki.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Additional tests for UtfNormal::cleanUp() function, inclusion
+ * regression checks for known problems.
+ * Requires PHPUnit.
+ *
+ * @ingroup UtfNormal
+ */
+class CleanUpTest extends MediaWikiTestCase {
+ /** @todo document */
+ function testAscii() {
+ $text = 'This is plain ASCII text.';
+ $this->assertEquals( $text, UtfNormal::cleanUp( $text ) );
+ }
+
+ /** @todo document */
+ function testNull() {
+ $text = "a \x00 null";
+ $expect = "a \xef\xbf\xbd null";
+ $this->assertEquals(
+ bin2hex( $expect ),
+ bin2hex( UtfNormal::cleanUp( $text ) ) );
+ }
+
+ /** @todo document */
+ function testLatin() {
+ $text = "L'\xc3\xa9cole";
+ $this->assertEquals( $text, UtfNormal::cleanUp( $text ) );
+ }
+
+ /** @todo document */
+ function testLatinNormal() {
+ $text = "L'e\xcc\x81cole";
+ $expect = "L'\xc3\xa9cole";
+ $this->assertEquals( $expect, UtfNormal::cleanUp( $text ) );
+ }
+
+ /**
+ * This test is *very* expensive!
+ * @todo document
+ */
+ function XtestAllChars() {
+ $rep = UTF8_REPLACEMENT;
+ for( $i = 0x0; $i < UNICODE_MAX; $i++ ) {
+ $char = codepointToUtf8( $i );
+ $clean = UtfNormal::cleanUp( $char );
+ $x = sprintf( "%04X", $i );
+ if( $i % 0x1000 == 0 ) echo "U+$x\n";
+ if( $i == 0x0009 ||
+ $i == 0x000a ||
+ $i == 0x000d ||
+ ($i > 0x001f && $i < UNICODE_SURROGATE_FIRST) ||
+ ($i > UNICODE_SURROGATE_LAST && $i < 0xfffe ) ||
+ ($i > 0xffff && $i <= UNICODE_MAX ) ) {
+ if( isset( UtfNormal::$utfCanonicalComp[$char] ) || isset( UtfNormal::$utfCanonicalDecomp[$char] ) ) {
+ $comp = UtfNormal::NFC( $char );
+ $this->assertEquals(
+ bin2hex( $comp ),
+ bin2hex( $clean ),
+ "U+$x should be decomposed" );
+ } else {
+ $this->assertEquals(
+ bin2hex( $char ),
+ bin2hex( $clean ),
+ "U+$x should be intact" );
+ }
+ } else {
+ $this->assertEquals( bin2hex( $rep ), bin2hex( $clean ), $x );
+ }
+ }
+ }
+
+ /** @todo document */
+ function testAllBytes() {
+ $this->doTestBytes( '', '' );
+ $this->doTestBytes( 'x', '' );
+ $this->doTestBytes( '', 'x' );
+ $this->doTestBytes( 'x', 'x' );
+ }
+
+ /** @todo document */
+ function doTestBytes( $head, $tail ) {
+ for( $i = 0x0; $i < 256; $i++ ) {
+ $char = $head . chr( $i ) . $tail;
+ $clean = UtfNormal::cleanUp( $char );
+ $x = sprintf( "%02X", $i );
+ if( $i == 0x0009 ||
+ $i == 0x000a ||
+ $i == 0x000d ||
+ ($i > 0x001f && $i < 0x80) ) {
+ $this->assertEquals(
+ bin2hex( $char ),
+ bin2hex( $clean ),
+ "ASCII byte $x should be intact" );
+ if( $char != $clean ) return;
+ } else {
+ $norm = $head . UTF8_REPLACEMENT . $tail;
+ $this->assertEquals(
+ bin2hex( $norm ),
+ bin2hex( $clean ),
+ "Forbidden byte $x should be rejected" );
+ if( $norm != $clean ) return;
+ }
+ }
+ }
+
+ /** @todo document */
+ function testDoubleBytes() {
+ $this->doTestDoubleBytes( '', '' );
+ $this->doTestDoubleBytes( 'x', '' );
+ $this->doTestDoubleBytes( '', 'x' );
+ $this->doTestDoubleBytes( 'x', 'x' );
+ }
+
+ /**
+ * @todo document
+ */
+ function doTestDoubleBytes( $head, $tail ) {
+ for( $first = 0xc0; $first < 0x100; $first+=2 ) {
+ for( $second = 0x80; $second < 0x100; $second+=2 ) {
+ $char = $head . chr( $first ) . chr( $second ) . $tail;
+ $clean = UtfNormal::cleanUp( $char );
+ $x = sprintf( "%02X,%02X", $first, $second );
+ if( $first > 0xc1 &&
+ $first < 0xe0 &&
+ $second < 0xc0 ) {
+ $norm = UtfNormal::NFC( $char );
+ $this->assertEquals(
+ bin2hex( $norm ),
+ bin2hex( $clean ),
+ "Pair $x should be intact" );
+ if( $norm != $clean ) return;
+ } elseif( $first > 0xfd || $second > 0xbf ) {
+ # fe and ff are not legal head bytes -- expect two replacement chars
+ $norm = $head . UTF8_REPLACEMENT . UTF8_REPLACEMENT . $tail;
+ $this->assertEquals(
+ bin2hex( $norm ),
+ bin2hex( $clean ),
+ "Forbidden pair $x should be rejected" );
+ if( $norm != $clean ) return;
+ } else {
+ $norm = $head . UTF8_REPLACEMENT . $tail;
+ $this->assertEquals(
+ bin2hex( $norm ),
+ bin2hex( $clean ),
+ "Forbidden pair $x should be rejected" );
+ if( $norm != $clean ) return;
+ }
+ }
+ }
+ }
+
+ /** @todo document */
+ function testTripleBytes() {
+ $this->doTestTripleBytes( '', '' );
+ $this->doTestTripleBytes( 'x', '' );
+ $this->doTestTripleBytes( '', 'x' );
+ $this->doTestTripleBytes( 'x', 'x' );
+ }
+
+ /** @todo document */
+ function doTestTripleBytes( $head, $tail ) {
+ for( $first = 0xc0; $first < 0x100; $first+=2 ) {
+ for( $second = 0x80; $second < 0x100; $second+=2 ) {
+ #for( $third = 0x80; $third < 0x100; $third++ ) {
+ for( $third = 0x80; $third < 0x81; $third++ ) {
+ $char = $head . chr( $first ) . chr( $second ) . chr( $third ) . $tail;
+ $clean = UtfNormal::cleanUp( $char );
+ $x = sprintf( "%02X,%02X,%02X", $first, $second, $third );
+ if( $first >= 0xe0 &&
+ $first < 0xf0 &&
+ $second < 0xc0 &&
+ $third < 0xc0 ) {
+ if( $first == 0xe0 && $second < 0xa0 ) {
+ $this->assertEquals(
+ bin2hex( $head . UTF8_REPLACEMENT . $tail ),
+ bin2hex( $clean ),
+ "Overlong triplet $x should be rejected" );
+ } elseif( $first == 0xed &&
+ ( chr( $first ) . chr( $second ) . chr( $third )) >= UTF8_SURROGATE_FIRST ) {
+ $this->assertEquals(
+ bin2hex( $head . UTF8_REPLACEMENT . $tail ),
+ bin2hex( $clean ),
+ "Surrogate triplet $x should be rejected" );
+ } else {
+ $this->assertEquals(
+ bin2hex( UtfNormal::NFC( $char ) ),
+ bin2hex( $clean ),
+ "Triplet $x should be intact" );
+ }
+ } elseif( $first > 0xc1 && $first < 0xe0 && $second < 0xc0 ) {
+ $this->assertEquals(
+ bin2hex( UtfNormal::NFC( $head . chr( $first ) . chr( $second ) ) . UTF8_REPLACEMENT . $tail ),
+ bin2hex( $clean ),
+ "Valid 2-byte $x + broken tail" );
+ } elseif( $second > 0xc1 && $second < 0xe0 && $third < 0xc0 ) {
+ $this->assertEquals(
+ bin2hex( $head . UTF8_REPLACEMENT . UtfNormal::NFC( chr( $second ) . chr( $third ) . $tail ) ),
+ bin2hex( $clean ),
+ "Broken head + valid 2-byte $x" );
+ } elseif( ( $first > 0xfd || $second > 0xfd ) &&
+ ( ( $second > 0xbf && $third > 0xbf ) ||
+ ( $second < 0xc0 && $third < 0xc0 ) ||
+ ( $second > 0xfd ) ||
+ ( $third > 0xfd ) ) ) {
+ # fe and ff are not legal head bytes -- expect three replacement chars
+ $this->assertEquals(
+ bin2hex( $head . UTF8_REPLACEMENT . UTF8_REPLACEMENT . UTF8_REPLACEMENT . $tail ),
+ bin2hex( $clean ),
+ "Forbidden triplet $x should be rejected" );
+ } elseif( $first > 0xc2 && $second < 0xc0 && $third < 0xc0 ) {
+ $this->assertEquals(
+ bin2hex( $head . UTF8_REPLACEMENT . $tail ),
+ bin2hex( $clean ),
+ "Forbidden triplet $x should be rejected" );
+ } else {
+ $this->assertEquals(
+ bin2hex( $head . UTF8_REPLACEMENT . UTF8_REPLACEMENT . $tail ),
+ bin2hex( $clean ),
+ "Forbidden triplet $x should be rejected" );
+ }
+ }
+ }
+ }
+ }
+
+ /** @todo document */
+ function testChunkRegression() {
+ # Check for regression against a chunking bug
+ $text = "\x46\x55\xb8" .
+ "\xdc\x96" .
+ "\xee" .
+ "\xe7" .
+ "\x44" .
+ "\xaa" .
+ "\x2f\x25";
+ $expect = "\x46\x55\xef\xbf\xbd" .
+ "\xdc\x96" .
+ "\xef\xbf\xbd" .
+ "\xef\xbf\xbd" .
+ "\x44" .
+ "\xef\xbf\xbd" .
+ "\x2f\x25";
+
+ $this->assertEquals(
+ bin2hex( $expect ),
+ bin2hex( UtfNormal::cleanUp( $text ) ) );
+ }
+
+ /** @todo document */
+ function testInterposeRegression() {
+ $text = "\x4e\x30" .
+ "\xb1" . # bad tail
+ "\x3a" .
+ "\x92" . # bad tail
+ "\x62\x3a" .
+ "\x84" . # bad tail
+ "\x43" .
+ "\xc6" . # bad head
+ "\x3f" .
+ "\x92" . # bad tail
+ "\xad" . # bad tail
+ "\x7d" .
+ "\xd9\x95";
+
+ $expect = "\x4e\x30" .
+ "\xef\xbf\xbd" .
+ "\x3a" .
+ "\xef\xbf\xbd" .
+ "\x62\x3a" .
+ "\xef\xbf\xbd" .
+ "\x43" .
+ "\xef\xbf\xbd" .
+ "\x3f" .
+ "\xef\xbf\xbd" .
+ "\xef\xbf\xbd" .
+ "\x7d" .
+ "\xd9\x95";
+
+ $this->assertEquals(
+ bin2hex( $expect ),
+ bin2hex( UtfNormal::cleanUp( $text ) ) );
+ }
+
+ /** @todo document */
+ function testOverlongRegression() {
+ $text = "\x67" .
+ "\x1a" . # forbidden ascii
+ "\xea" . # bad head
+ "\xc1\xa6" . # overlong sequence
+ "\xad" . # bad tail
+ "\x1c" . # forbidden ascii
+ "\xb0" . # bad tail
+ "\x3c" .
+ "\x9e"; # bad tail
+ $expect = "\x67" .
+ "\xef\xbf\xbd" .
+ "\xef\xbf\xbd" .
+ "\xef\xbf\xbd" .
+ "\xef\xbf\xbd" .
+ "\xef\xbf\xbd" .
+ "\xef\xbf\xbd" .
+ "\x3c" .
+ "\xef\xbf\xbd";
+ $this->assertEquals(
+ bin2hex( $expect ),
+ bin2hex( UtfNormal::cleanUp( $text ) ) );
+ }
+
+ /** @todo document */
+ function testSurrogateRegression() {
+ $text = "\xed\xb4\x96" . # surrogate 0xDD16
+ "\x83" . # bad tail
+ "\xb4" . # bad tail
+ "\xac"; # bad head
+ $expect = "\xef\xbf\xbd" .
+ "\xef\xbf\xbd" .
+ "\xef\xbf\xbd" .
+ "\xef\xbf\xbd";
+ $this->assertEquals(
+ bin2hex( $expect ),
+ bin2hex( UtfNormal::cleanUp( $text ) ) );
+ }
+
+ /** @todo document */
+ function testBomRegression() {
+ $text = "\xef\xbf\xbe" . # U+FFFE, illegal char
+ "\xb2" . # bad tail
+ "\xef" . # bad head
+ "\x59";
+ $expect = "\xef\xbf\xbd" .
+ "\xef\xbf\xbd" .
+ "\xef\xbf\xbd" .
+ "\x59";
+ $this->assertEquals(
+ bin2hex( $expect ),
+ bin2hex( UtfNormal::cleanUp( $text ) ) );
+ }
+
+ /** @todo document */
+ function testForbiddenRegression() {
+ $text = "\xef\xbf\xbf"; # U+FFFF, illegal char
+ $expect = "\xef\xbf\xbd";
+ $this->assertEquals(
+ bin2hex( $expect ),
+ bin2hex( UtfNormal::cleanUp( $text ) ) );
+ }
+
+ /** @todo document */
+ function testHangulRegression() {
+ $text = "\xed\x9c\xaf" . # Hangul char
+ "\xe1\x87\x81"; # followed by another final jamo
+ $expect = $text; # Should *not* change.
+ $this->assertEquals(
+ bin2hex( $expect ),
+ bin2hex( UtfNormal::cleanUp( $text ) ) );
+ }
+}
diff --git a/tests/phpunit/includes/parser/MagicVariableTest.php b/tests/phpunit/includes/parser/MagicVariableTest.php
new file mode 100644
index 00000000..a47653e3
--- /dev/null
+++ b/tests/phpunit/includes/parser/MagicVariableTest.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * This file is intended to test magic variables in the parser
+ * It was inspired by Raymond & Matěj Grabovský commenting about r66200
+ *
+ * As of february 2011, it only tests some revisions and date related
+ * magic variables.
+ *
+ * @author Ashar Voultoiz
+ * @copyright Copyright © 2011, Ashar Voultoiz
+ * @file
+ */
+
+/** */
+class MagicVariableTest extends MediaWikiTestCase {
+ /** Will contains a parser object*/
+ private $testParser = null;
+
+ /**
+ * An array of magicword returned as type integer by the parser
+ * They are usually returned as a string for i18n since we support
+ * persan numbers for example, but some magic explicitly return
+ * them as integer.
+ * @see MagicVariableTest::assertMagic()
+ */
+ private $expectedAsInteger = array(
+ 'revisionday',
+ 'revisionmonth1',
+ );
+
+ /** setup a basic parser object */
+ function setUp() {
+ global $wgContLang;
+ $wgContLang = Language::factory( 'en' );
+
+ $this->testParser = new Parser();
+ $this->testParser->Options( new ParserOptions() );
+
+ # initialize parser output
+ $this->testParser->clearState();
+ }
+
+ /** destroy parser (TODO: is it really neded?)*/
+ function tearDown() {
+ unset( $this->testParser );
+ }
+
+ ############### TESTS #############################################
+ # @todo FIXME:
+ # - those got copy pasted, we can probably make them cleaner
+ # - tests are lacking useful messages
+
+ # day
+
+ /** @dataProvider MediaWikiProvide::Days */
+ function testCurrentdayIsUnPadded( $day ) {
+ $this->assertUnPadded( 'currentday', $day );
+ }
+ /** @dataProvider MediaWikiProvide::Days */
+ function testCurrentdaytwoIsZeroPadded( $day ) {
+ $this->assertZeroPadded( 'currentday2', $day );
+ }
+ /** @dataProvider MediaWikiProvide::Days */
+ function testLocaldayIsUnPadded( $day ) {
+ $this->assertUnPadded( 'localday', $day );
+ }
+ /** @dataProvider MediaWikiProvide::Days */
+ function testLocaldaytwoIsZeroPadded( $day ) {
+ $this->assertZeroPadded( 'localday2', $day );
+ }
+
+ # month
+
+ /** @dataProvider MediaWikiProvide::Months */
+ function testCurrentmonthIsZeroPadded( $month ) {
+ $this->assertZeroPadded( 'currentmonth', $month );
+ }
+ /** @dataProvider MediaWikiProvide::Months */
+ function testCurrentmonthoneIsUnPadded( $month ) {
+ $this->assertUnPadded( 'currentmonth1', $month );
+ }
+ /** @dataProvider MediaWikiProvide::Months */
+ function testLocalmonthIsZeroPadded( $month ) {
+ $this->assertZeroPadded( 'localmonth', $month );
+ }
+ /** @dataProvider MediaWikiProvide::Months */
+ function testLocalmonthoneIsUnPadded( $month ) {
+ $this->assertUnPadded( 'localmonth1', $month );
+ }
+
+
+ # revision day
+
+ /** @dataProvider MediaWikiProvide::Days */
+ function testRevisiondayIsUnPadded( $day ) {
+ $this->assertUnPadded( 'revisionday', $day );
+ }
+ /** @dataProvider MediaWikiProvide::Days */
+ function testRevisiondaytwoIsZeroPadded( $day ) {
+ $this->assertZeroPadded( 'revisionday2', $day );
+ }
+
+ # revision month
+
+ /** @dataProvider MediaWikiProvide::Months */
+ function testRevisionmonthIsZeroPadded( $month ) {
+ $this->assertZeroPadded( 'revisionmonth', $month );
+ }
+ /** @dataProvider MediaWikiProvide::Months */
+ function testRevisionmonthoneIsUnPadded( $month ) {
+ $this->assertUnPadded( 'revisionmonth1', $month );
+ }
+
+ /**
+ * Rough tests for {{SERVERNAME}} magic word
+ * Bug 31176
+ */
+ function testServernameFromDifferentProtocols() {
+ global $wgServer;
+ $saved_wgServer= $wgServer;
+
+ $wgServer = 'http://localhost/';
+ $this->assertMagic( 'localhost', 'servername' );
+ $wgServer = 'https://localhost/';
+ $this->assertMagic( 'localhost', 'servername' );
+ $wgServer = '//localhost/'; # bug 31176
+ $this->assertMagic( 'localhost', 'servername' );
+
+ $wgServer = $saved_wgServer;
+ }
+
+ ############### HELPERS ############################################
+
+ /** assertion helper expecting a magic output which is zero padded */
+ PUBLIC function assertZeroPadded( $magic, $value ) {
+ $this->assertMagicPadding( $magic, $value, '%02d' );
+ }
+
+ /** assertion helper expecting a magic output which is unpadded */
+ PUBLIC function assertUnPadded( $magic, $value ) {
+ $this->assertMagicPadding( $magic, $value, '%d' );
+ }
+
+ /**
+ * Main assertion helper for magic variables padding
+ * @param $magic string Magic variable name
+ * @param $value mixed Month or day
+ * @param $format string sprintf format for $value
+ */
+ private function assertMagicPadding( $magic, $value, $format ) {
+ # Initialize parser timestamp as year 2010 at 12h34 56s.
+ # month and day are given by the caller ($value). Month < 12!
+ if( $value > 12 ) { $month = $value % 12; }
+ else { $month = $value; }
+
+ $this->setParserTS(
+ sprintf( '2010%02d%02d123456', $month, $value )
+ );
+
+ # please keep the following commented line of code. It helps debugging.
+ //print "\nDEBUG (value $value):" . sprintf( '2010%02d%02d123456', $value, $value ) . "\n";
+
+ # format expectation and test it
+ $expected = sprintf( $format, $value );
+ $this->assertMagic( $expected, $magic );
+ }
+
+ /** helper to set the parser timestamp and revision timestamp */
+ private function setParserTS( $ts ) {
+ $this->testParser->Options()->setTimestamp( $ts );
+ $this->testParser->mRevisionTimestamp = $ts;
+ }
+
+ /**
+ * Assertion helper to test a magic variable output
+ */
+ private function assertMagic( $expected, $magic ) {
+ if( in_array( $magic, $this->expectedAsInteger ) ) {
+ $expected = (int) $expected;
+ }
+
+ # Generate a message for the assertion
+ $msg = sprintf( "Magic %s should be <%s:%s>",
+ $magic,
+ $expected,
+ gettype( $expected )
+ );
+
+ $this->assertSame(
+ $expected,
+ $this->testParser->getVariableValue( $magic ),
+ $msg
+ );
+ }
+}
diff --git a/tests/phpunit/includes/parser/MediaWikiParserTest.php b/tests/phpunit/includes/parser/MediaWikiParserTest.php
new file mode 100644
index 00000000..18510d9a
--- /dev/null
+++ b/tests/phpunit/includes/parser/MediaWikiParserTest.php
@@ -0,0 +1,34 @@
+<?php
+
+require_once( dirname( __FILE__ ) . '/ParserHelpers.php' );
+require_once( dirname( __FILE__ ) . '/NewParserTest.php' );
+
+/**
+ * The UnitTest must be either a class that inherits from PHPUnit_Framework_TestCase
+ * or a class that provides a public static suite() method which returns
+ * an PHPUnit_Framework_Test object
+ *
+ * @group Parser
+ * @group Database
+ */
+class MediaWikiParserTest {
+
+ public static function suite() {
+ global $wgParserTestFiles;
+
+ $suite = new PHPUnit_Framework_TestSuite;
+
+ foreach ( $wgParserTestFiles as $filename ) {
+ $testsName = basename( $filename, '.txt' );
+ $className = /*ucfirst( basename( dirname( $filename ) ) ) .*/ ucfirst( basename( $filename, '.txt' ) );
+
+ eval( "/** @group Database\n@group Parser\n*/ class $className extends NewParserTest { protected \$file = \"" . addslashes( $filename ) . "\"; } " );
+
+ $parserTester = new $className( $testsName );
+ $suite->addTestSuite( new ReflectionClass ( $parserTester ) );
+ }
+
+
+ return $suite;
+ }
+}
diff --git a/tests/phpunit/includes/parser/NewParserTest.php b/tests/phpunit/includes/parser/NewParserTest.php
new file mode 100644
index 00000000..f4d5f757
--- /dev/null
+++ b/tests/phpunit/includes/parser/NewParserTest.php
@@ -0,0 +1,850 @@
+<?php
+
+/**
+ * Although marked as a stub, can work independently.
+ *
+ * @group Database
+ * @group Parser
+ * @group Stub
+ */
+class NewParserTest extends MediaWikiTestCase {
+
+ static protected $articles = array(); // Array of test articles defined by the tests
+ /* The dataProvider is run on a different instance than the test, so it must be static
+ * When running tests from several files, all tests will see all articles.
+ */
+
+ public $uploadDir;
+ public $keepUploads = false;
+ public $runDisabled = false;
+ public $regex = '';
+ public $showProgress = true;
+ public $savedInitialGlobals = array();
+ public $savedWeirdGlobals = array();
+ public $savedGlobals = array();
+ public $hooks = array();
+ public $functionHooks = array();
+
+ //Fuzz test
+ public $maxFuzzTestLength = 300;
+ public $fuzzSeed = 0;
+ public $memoryLimit = 50;
+
+ protected $file = false;
+
+ /*function __construct($a = null,$b = array(),$c = null ) {
+ parent::__construct($a,$b,$c);
+ }*/
+
+ function setUp() {
+ global $wgContLang, $wgNamespaceProtection, $wgNamespaceAliases;
+ global $wgHooks, $IP;
+ $wgContLang = Language::factory( 'en' );
+
+ //Setup CLI arguments
+ if ( $this->getCliArg( 'regex=' ) ) {
+ $this->regex = $this->getCliArg( 'regex=' );
+ } else {
+ # Matches anything
+ $this->regex = '';
+ }
+
+ $this->keepUploads = $this->getCliArg( 'keep-uploads' );
+
+ $tmpGlobals = array();
+
+ $tmpGlobals['wgScript'] = '/index.php';
+ $tmpGlobals['wgScriptPath'] = '/';
+ $tmpGlobals['wgArticlePath'] = '/wiki/$1';
+ $tmpGlobals['wgStyleSheetPath'] = '/skins';
+ $tmpGlobals['wgStylePath'] = '/skins';
+ $tmpGlobals['wgThumbnailScriptPath'] = false;
+ $tmpGlobals['wgLocalFileRepo'] = array(
+ 'class' => 'LocalRepo',
+ 'name' => 'local',
+ 'directory' => wfTempDir() . '/test-repo',
+ 'url' => 'http://example.com/images',
+ 'deletedDir' => wfTempDir() . '/test-repo/delete',
+ 'hashLevels' => 2,
+ 'transformVia404' => false,
+ );
+
+ $tmpGlobals['wgEnableParserCache'] = false;
+ $tmpGlobals['wgHooks'] = $wgHooks;
+ $tmpGlobals['wgDeferredUpdateList'] = array();
+ $tmpGlobals['wgMemc'] = wfGetMainCache();
+ $tmpGlobals['messageMemc'] = wfGetMessageCacheStorage();
+ $tmpGlobals['parserMemc'] = wfGetParserCacheStorage();
+
+ // $tmpGlobals['wgContLang'] = new StubContLang;
+ $tmpGlobals['wgUser'] = new User;
+ $context = new RequestContext();
+ $tmpGlobals['wgLang'] = $context->getLang();
+ $tmpGlobals['wgOut'] = $context->getOutput();
+ $tmpGlobals['wgParser'] = new StubObject( 'wgParser', $GLOBALS['wgParserConf']['class'], array( $GLOBALS['wgParserConf'] ) );
+ $tmpGlobals['wgRequest'] = new WebRequest;
+
+ if ( $GLOBALS['wgStyleDirectory'] === false ) {
+ $tmpGlobals['wgStyleDirectory'] = "$IP/skins";
+ }
+
+
+ foreach ( $tmpGlobals as $var => $val ) {
+ if ( array_key_exists( $var, $GLOBALS ) ) {
+ $this->savedInitialGlobals[$var] = $GLOBALS[$var];
+ }
+
+ $GLOBALS[$var] = $val;
+ }
+
+ $this->savedWeirdGlobals['mw_namespace_protection'] = $wgNamespaceProtection[NS_MEDIAWIKI];
+ $this->savedWeirdGlobals['image_alias'] = $wgNamespaceAliases['Image'];
+ $this->savedWeirdGlobals['image_talk_alias'] = $wgNamespaceAliases['Image_talk'];
+
+ $wgNamespaceProtection[NS_MEDIAWIKI] = 'editinterface';
+ $wgNamespaceAliases['Image'] = NS_FILE;
+ $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK;
+
+ }
+
+ public function tearDown() {
+
+ foreach ( $this->savedInitialGlobals as $var => $val ) {
+ $GLOBALS[$var] = $val;
+ }
+
+ global $wgNamespaceProtection, $wgNamespaceAliases;
+
+ $wgNamespaceProtection[NS_MEDIAWIKI] = $this->savedWeirdGlobals['mw_namespace_protection'];
+ $wgNamespaceAliases['Image'] = $this->savedWeirdGlobals['image_alias'];
+ $wgNamespaceAliases['Image_talk'] = $this->savedWeirdGlobals['image_talk_alias'];
+ }
+
+ function addDBData() {
+ # Hack: insert a few Wikipedia in-project interwiki prefixes,
+ # for testing inter-language links
+ $this->db->insert( 'interwiki', array(
+ array( 'iw_prefix' => 'wikipedia',
+ 'iw_url' => 'http://en.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 0 ),
+ array( 'iw_prefix' => 'meatball',
+ 'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 0 ),
+ array( 'iw_prefix' => 'zh',
+ 'iw_url' => 'http://zh.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 1 ),
+ array( 'iw_prefix' => 'es',
+ 'iw_url' => 'http://es.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 1 ),
+ array( 'iw_prefix' => 'fr',
+ 'iw_url' => 'http://fr.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 1 ),
+ array( 'iw_prefix' => 'ru',
+ 'iw_url' => 'http://ru.wikipedia.org/wiki/$1',
+ 'iw_api' => '',
+ 'iw_wikiid' => '',
+ 'iw_local' => 1 ),
+ /**
+ * @todo Fixme! Why are we inserting duplicate data here? Shouldn't
+ * need this IGNORE or shouldn't need the insert at all.
+ */
+ ), __METHOD__, array( 'IGNORE' ) );
+
+
+ # Update certain things in site_stats
+ $this->db->insert( 'site_stats',
+ array( 'ss_row_id' => 1, 'ss_images' => 2, 'ss_good_articles' => 1 ),
+ __METHOD__,
+ /**
+ * @todo Fixme! Same as above!
+ */
+ array( 'IGNORE' )
+ );
+
+ # Reinitialise the LocalisationCache to match the database state
+ Language::getLocalisationCache()->unloadAll();
+
+ # Clear the message cache
+ MessageCache::singleton()->clear();
+
+ $this->uploadDir = $this->setupUploadDir();
+
+ $user = User::newFromId( 0 );
+ LinkCache::singleton()->clear(); # Avoids the odd failure at creating the nullRevision
+
+ $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
+ $image->recordUpload2( '', 'Upload of some lame file', 'Some lame file', array(
+ 'size' => 12345,
+ 'width' => 1941,
+ 'height' => 220,
+ 'bits' => 24,
+ 'media_type' => MEDIATYPE_BITMAP,
+ 'mime' => 'image/jpeg',
+ 'metadata' => serialize( array() ),
+ 'sha1' => wfBaseConvert( '', 16, 36, 31 ),
+ 'fileExists' => true
+ ), $this->db->timestamp( '20010115123500' ), $user );
+
+ # This image will be blacklisted in [[MediaWiki:Bad image list]]
+ $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
+ $image->recordUpload2( '', 'zomgnotcensored', 'Borderline image', array(
+ 'size' => 12345,
+ 'width' => 320,
+ 'height' => 240,
+ 'bits' => 24,
+ 'media_type' => MEDIATYPE_BITMAP,
+ 'mime' => 'image/jpeg',
+ 'metadata' => serialize( array() ),
+ 'sha1' => wfBaseConvert( '', 16, 36, 31 ),
+ 'fileExists' => true
+ ), $this->db->timestamp( '20010115123500' ), $user );
+
+ }
+
+
+
+
+ //ParserTest setup/teardown functions
+
+ /**
+ * Set up the global variables for a consistent environment for each test.
+ * Ideally this should replace the global configuration entirely.
+ */
+ protected function setupGlobals( $opts = '', $config = '' ) {
+ # Find out values for some special options.
+ $lang =
+ self::getOptionValue( 'language', $opts, 'en' );
+ $variant =
+ self::getOptionValue( 'variant', $opts, false );
+ $maxtoclevel =
+ self::getOptionValue( 'wgMaxTocLevel', $opts, 999 );
+ $linkHolderBatchSize =
+ self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 );
+
+ $settings = array(
+ 'wgServer' => 'http://Britney-Spears',
+ 'wgScript' => '/index.php',
+ 'wgScriptPath' => '/',
+ 'wgArticlePath' => '/wiki/$1',
+ 'wgActionPaths' => array(),
+ 'wgLocalFileRepo' => array(
+ 'class' => 'LocalRepo',
+ 'name' => 'local',
+ 'directory' => $this->uploadDir,
+ 'url' => 'http://example.com/images',
+ 'hashLevels' => 2,
+ 'transformVia404' => false,
+ ),
+ 'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ),
+ 'wgStylePath' => '/skins',
+ 'wgStyleSheetPath' => '/skins',
+ 'wgSitename' => 'MediaWiki',
+ 'wgLanguageCode' => $lang,
+ 'wgDBprefix' => $this->db->getType() != 'oracle' ? 'unittest_' : 'ut_',
+ 'wgRawHtml' => isset( $opts['rawhtml'] ),
+ 'wgLang' => null,
+ 'wgContLang' => null,
+ 'wgNamespacesWithSubpages' => array( 0 => isset( $opts['subpage'] ) ),
+ 'wgMaxTocLevel' => $maxtoclevel,
+ 'wgCapitalLinks' => true,
+ 'wgNoFollowLinks' => true,
+ 'wgNoFollowDomainExceptions' => array(),
+ 'wgThumbnailScriptPath' => false,
+ 'wgUseImageResize' => false,
+ 'wgUseTeX' => isset( $opts['math'] ),
+ 'wgMathDirectory' => $this->uploadDir . '/math',
+ 'wgLocaltimezone' => 'UTC',
+ 'wgAllowExternalImages' => true,
+ 'wgUseTidy' => false,
+ 'wgDefaultLanguageVariant' => $variant,
+ 'wgVariantArticlePath' => false,
+ 'wgGroupPermissions' => array( '*' => array(
+ 'createaccount' => true,
+ 'read' => true,
+ 'edit' => true,
+ 'createpage' => true,
+ 'createtalk' => true,
+ ) ),
+ 'wgNamespaceProtection' => array( NS_MEDIAWIKI => 'editinterface' ),
+ 'wgDefaultExternalStore' => array(),
+ 'wgForeignFileRepos' => array(),
+ 'wgLinkHolderBatchSize' => $linkHolderBatchSize,
+ 'wgExperimentalHtmlIds' => false,
+ 'wgExternalLinkTarget' => false,
+ 'wgAlwaysUseTidy' => false,
+ 'wgHtml5' => true,
+ 'wgWellFormedXml' => true,
+ 'wgAllowMicrodataAttributes' => true,
+ 'wgAdaptiveMessageCache' => true,
+ 'wgUseDatabaseMessages' => true,
+ );
+
+ if ( $config ) {
+ $configLines = explode( "\n", $config );
+
+ foreach ( $configLines as $line ) {
+ list( $var, $value ) = explode( '=', $line, 2 );
+
+ $settings[$var] = eval( "return $value;" ); //???
+ }
+ }
+
+ $this->savedGlobals = array();
+
+ foreach ( $settings as $var => $val ) {
+ if ( array_key_exists( $var, $GLOBALS ) ) {
+ $this->savedGlobals[$var] = $GLOBALS[$var];
+ }
+
+ $GLOBALS[$var] = $val;
+ }
+
+ $langObj = Language::factory( $lang );
+ $GLOBALS['wgContLang'] = $langObj;
+ $context = new RequestContext();
+ $GLOBALS['wgLang'] = $context->getLang();
+
+ $GLOBALS['wgMemc'] = new EmptyBagOStuff;
+ $GLOBALS['wgOut'] = $context->getOutput();
+
+ global $wgHooks;
+
+ $wgHooks['ParserTestParser'][] = 'ParserTestParserHook::setup';
+ $wgHooks['ParserTestParser'][] = 'ParserTestStaticParserHook::setup';
+ $wgHooks['ParserGetVariableValueTs'][] = 'ParserTest::getFakeTimestamp';
+
+ MagicWord::clearCache();
+
+ # Publish the articles after we have the final language set
+ $this->publishTestArticles();
+
+ # The entries saved into RepoGroup cache with previous globals will be wrong.
+ RepoGroup::destroySingleton();
+ MessageCache::singleton()->destroyInstance();
+
+ global $wgUser;
+ $wgUser = new User();
+ }
+
+ /**
+ * Create a dummy uploads directory which will contain a couple
+ * of files in order to pass existence tests.
+ *
+ * @return String: the directory
+ */
+ protected function setupUploadDir() {
+ global $IP;
+
+ if ( $this->keepUploads ) {
+ $dir = wfTempDir() . '/mwParser-images';
+
+ if ( is_dir( $dir ) ) {
+ return $dir;
+ }
+ } else {
+ $dir = wfTempDir() . "/mwParser-" . mt_rand() . "-images";
+ }
+
+ // wfDebug( "Creating upload directory $dir\n" );
+ if ( file_exists( $dir ) ) {
+ wfDebug( "Already exists!\n" );
+ return $dir;
+ }
+
+ wfMkdirParents( $dir . '/3/3a' );
+ copy( "$IP/skins/monobook/headbg.jpg", "$dir/3/3a/Foobar.jpg" );
+ wfMkdirParents( $dir . '/0/09' );
+ copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" );
+
+ return $dir;
+ }
+
+ /**
+ * Restore default values and perform any necessary clean-up
+ * after each test runs.
+ */
+ protected function teardownGlobals() {
+ RepoGroup::destroySingleton();
+ LinkCache::singleton()->clear();
+
+ foreach ( $this->savedGlobals as $var => $val ) {
+ $GLOBALS[$var] = $val;
+ }
+
+ $this->teardownUploadDir( $this->uploadDir );
+ }
+
+ /**
+ * Remove the dummy uploads directory
+ */
+ private function teardownUploadDir( $dir ) {
+ if ( $this->keepUploads ) {
+ return;
+ }
+
+ // delete the files first, then the dirs.
+ self::deleteFiles(
+ array (
+ "$dir/3/3a/Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/200px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/640px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg",
+
+ "$dir/0/09/Bad.jpg",
+
+ "$dir/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
+ )
+ );
+
+ self::deleteDirs(
+ array (
+ "$dir/3/3a",
+ "$dir/3",
+ "$dir/thumb/6/65",
+ "$dir/thumb/6",
+ "$dir/thumb/3/3a/Foobar.jpg",
+ "$dir/thumb/3/3a",
+ "$dir/thumb/3",
+
+ "$dir/0/09/",
+ "$dir/0/",
+ "$dir/thumb",
+ "$dir/math/f/a/5",
+ "$dir/math/f/a",
+ "$dir/math/f",
+ "$dir/math",
+ "$dir",
+ )
+ );
+ }
+
+ /**
+ * Delete the specified files, if they exist.
+ * @param $files Array: full paths to files to delete.
+ */
+ private static function deleteFiles( $files ) {
+ foreach ( $files as $file ) {
+ if ( file_exists( $file ) ) {
+ unlink( $file );
+ }
+ }
+ }
+
+ /**
+ * Delete the specified directories, if they exist. Must be empty.
+ * @param $dirs Array: full paths to directories to delete.
+ */
+ private static function deleteDirs( $dirs ) {
+ foreach ( $dirs as $dir ) {
+ if ( is_dir( $dir ) ) {
+ rmdir( $dir );
+ }
+ }
+ }
+
+ public function parserTestProvider() {
+ if ( $this->file === false ) {
+ global $wgParserTestFiles;
+ $this->file = $wgParserTestFiles[0];
+ }
+ return new TestFileIterator( $this->file, $this );
+ }
+
+ /**
+ * Set the file from whose tests will be run by this instance
+ */
+ public function setParserTestFile( $filename ) {
+ $this->file = $filename;
+ }
+
+ /** @dataProvider parserTestProvider */
+ public function testParserTest( $desc, $input, $result, $opts, $config ) {
+ if ( !preg_match( '/' . $this->regex . '/', $desc ) ) return; //$this->markTestSkipped( 'Filtered out by the user' );
+
+ wfDebug( "Running parser test: $desc\n" );
+
+ $opts = $this->parseOptions( $opts );
+ $this->setupGlobals( $opts, $config );
+
+ $user = new User();
+ $options = ParserOptions::newFromUser( $user );
+
+ if ( isset( $opts['title'] ) ) {
+ $titleText = $opts['title'];
+ }
+ else {
+ $titleText = 'Parser test';
+ }
+
+ $local = isset( $opts['local'] );
+ $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null;
+ $parser = $this->getParser( $preprocessor );
+
+ $title = Title::newFromText( $titleText );
+
+ if ( isset( $opts['pst'] ) ) {
+ $out = $parser->preSaveTransform( $input, $title, $user, $options );
+ } elseif ( isset( $opts['msg'] ) ) {
+ $out = $parser->transformMsg( $input, $options, $title );
+ } elseif ( isset( $opts['section'] ) ) {
+ $section = $opts['section'];
+ $out = $parser->getSection( $input, $section );
+ } elseif ( isset( $opts['replace'] ) ) {
+ $section = $opts['replace'][0];
+ $replace = $opts['replace'][1];
+ $out = $parser->replaceSection( $input, $section, $replace );
+ } elseif ( isset( $opts['comment'] ) ) {
+ $linker = $user->getSkin();
+ $out = $linker->formatComment( $input, $title, $local );
+ } elseif ( isset( $opts['preload'] ) ) {
+ $out = $parser->getpreloadText( $input, $title, $options );
+ } else {
+ $output = $parser->parse( $input, $title, $options, true, true, 1337 );
+ $out = $output->getText();
+
+ if ( isset( $opts['showtitle'] ) ) {
+ if ( $output->getTitleText() ) {
+ $title = $output->getTitleText();
+ }
+
+ $out = "$title\n$out";
+ }
+
+ if ( isset( $opts['ill'] ) ) {
+ $out = $this->tidy( implode( ' ', $output->getLanguageLinks() ) );
+ } elseif ( isset( $opts['cat'] ) ) {
+ global $wgOut;
+
+ $wgOut->addCategoryLinks( $output->getCategories() );
+ $cats = $wgOut->getCategoryLinks();
+
+ if ( isset( $cats['normal'] ) ) {
+ $out = $this->tidy( implode( ' ', $cats['normal'] ) );
+ } else {
+ $out = '';
+ }
+ }
+ $parser->mPreprocessor = null;
+
+ $result = $this->tidy( $result );
+ }
+
+ $this->teardownGlobals();
+
+ $this->assertEquals( $result, $out, $desc );
+ }
+
+ /**
+ * Run a fuzz test series
+ * Draw input from a set of test files
+ */
+ function testFuzzTests() {
+
+ $this->markTestIncomplete( "Somebody is serializing PDO objects, that's a no-no" );
+
+ global $wgParserTestFiles;
+
+ $files = $wgParserTestFiles;
+
+ if( $this->getCliArg( 'file=' ) ) {
+ $files = array( $this->getCliArg( 'file=' ) );
+ }
+
+ $dict = $this->getFuzzInput( $files );
+ $dictSize = strlen( $dict );
+ $logMaxLength = log( $this->maxFuzzTestLength );
+
+ $user = new User;
+ $opts = ParserOptions::newFromUser( $user );
+ $title = Title::makeTitle( NS_MAIN, 'Parser_test' );
+
+ $id = 1;
+
+ while ( true ) {
+
+ // Generate test input
+ mt_srand( ++$this->fuzzSeed );
+ $totalLength = mt_rand( 1, $this->maxFuzzTestLength );
+ $input = '';
+
+ while ( strlen( $input ) < $totalLength ) {
+ $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength;
+ $hairLength = min( intval( exp( $logHairLength ) ), $dictSize );
+ $offset = mt_rand( 0, $dictSize - $hairLength );
+ $input .= substr( $dict, $offset, $hairLength );
+ }
+
+ $this->setupGlobals();
+ $parser = $this->getParser();
+
+ // Run the test
+ try {
+ $parser->parse( $input, $title, $opts );
+ $this->assertTrue( true, "Test $id, fuzz seed {$this->fuzzSeed}" );
+ } catch ( Exception $exception ) {
+ $input_dump = sprintf( "string(%d) \"%s\"\n", strlen( $input ), $input );
+
+ $this->assertTrue( false, "Test $id, fuzz seed {$this->fuzzSeed}. \n\nInput: $input_dump\n\nError: {$exception->getMessage()}\n\nBacktrace: {$exception->getTraceAsString()}" );
+ }
+
+ $this->teardownGlobals();
+ $parser->__destruct();
+
+ if ( $id % 100 == 0 ) {
+ $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 );
+ //echo "{$this->fuzzSeed}: $numSuccess/$numTotal (mem: $usage%)\n";
+ if ( $usage > 90 ) {
+ $ret = "Out of memory:\n";
+ $memStats = $this->getMemoryBreakdown();
+
+ foreach ( $memStats as $name => $usage ) {
+ $ret .= "$name: $usage\n";
+ }
+
+ throw new MWException( $ret );
+ }
+ }
+
+ $id++;
+
+ }
+ }
+
+ //Various getter functions
+
+ /**
+ * Get an input dictionary from a set of parser test files
+ */
+ function getFuzzInput( $filenames ) {
+ $dict = '';
+
+ foreach ( $filenames as $filename ) {
+ $contents = file_get_contents( $filename );
+ preg_match_all( '/!!\s*input\n(.*?)\n!!\s*result/s', $contents, $matches );
+
+ foreach ( $matches[1] as $match ) {
+ $dict .= $match . "\n";
+ }
+ }
+
+ return $dict;
+ }
+
+ /**
+ * Get a memory usage breakdown
+ */
+ function getMemoryBreakdown() {
+ $memStats = array();
+
+ foreach ( $GLOBALS as $name => $value ) {
+ $memStats['$' . $name] = strlen( serialize( $value ) );
+ }
+
+ $classes = get_declared_classes();
+
+ foreach ( $classes as $class ) {
+ $rc = new ReflectionClass( $class );
+ $props = $rc->getStaticProperties();
+ $memStats[$class] = strlen( serialize( $props ) );
+ $methods = $rc->getMethods();
+
+ foreach ( $methods as $method ) {
+ $memStats[$class] += strlen( serialize( $method->getStaticVariables() ) );
+ }
+ }
+
+ $functions = get_defined_functions();
+
+ foreach ( $functions['user'] as $function ) {
+ $rf = new ReflectionFunction( $function );
+ $memStats["$function()"] = strlen( serialize( $rf->getStaticVariables() ) );
+ }
+
+ asort( $memStats );
+
+ return $memStats;
+ }
+
+ /**
+ * Get a Parser object
+ */
+ function getParser( $preprocessor = null ) {
+ global $wgParserConf;
+
+ $class = $wgParserConf['class'];
+ $parser = new $class( array( 'preprocessorClass' => $preprocessor ) + $wgParserConf );
+
+ wfRunHooks( 'ParserTestParser', array( &$parser ) );
+
+ return $parser;
+ }
+
+ //Various action functions
+
+ public function addArticle( $name, $text, $line ) {
+ self::$articles[$name] = $text;
+ }
+
+ public function publishTestArticles() {
+ if ( empty( self::$articles ) ) {
+ return;
+ }
+
+ foreach ( self::$articles as $name => $text ) {
+ $title = Title::newFromText( $name );
+
+ if ( $title->getArticleID( Title::GAID_FOR_UPDATE ) == 0 ) {
+ ParserTest::addArticle( $name, $text );
+ }
+ }
+ }
+
+ /**
+ * Steal a callback function from the primary parser, save it for
+ * application to our scary parser. If the hook is not installed,
+ * abort processing of this file.
+ *
+ * @param $name String
+ * @return Bool true if tag hook is present
+ */
+ public function requireHook( $name ) {
+ global $wgParser;
+ $wgParser->firstCallInit( ); // make sure hooks are loaded.
+ return isset( $wgParser->mTagHooks[$name] );
+ }
+
+ public function requireFunctionHook( $name ) {
+ global $wgParser;
+ $wgParser->firstCallInit( ); // make sure hooks are loaded.
+ return isset( $wgParser->mFunctionHooks[$name] );
+ }
+ //Various "cleanup" functions
+
+ /*
+ * Run the "tidy" command on text if the $wgUseTidy
+ * global is true
+ *
+ * @param $text String: the text to tidy
+ * @return String
+ */
+ protected function tidy( $text ) {
+ global $wgUseTidy;
+
+ if ( $wgUseTidy ) {
+ $text = MWTidy::tidy( $text );
+ }
+
+ return $text;
+ }
+
+ /**
+ * Remove last character if it is a newline
+ */
+ public function removeEndingNewline( $s ) {
+ if ( substr( $s, -1 ) === "\n" ) {
+ return substr( $s, 0, -1 );
+ }
+ else {
+ return $s;
+ }
+ }
+
+ public function showRunFile( $file ) {
+ /* NOP */
+ }
+
+ //Test options parser functions
+
+ protected function parseOptions( $instring ) {
+ $opts = array();
+ // foo
+ // foo=bar
+ // foo="bar baz"
+ // foo=[[bar baz]]
+ // foo=bar,"baz quux"
+ $regex = '/\b
+ ([\w-]+) # Key
+ \b
+ (?:\s*
+ = # First sub-value
+ \s*
+ (
+ "
+ [^"]* # Quoted val
+ "
+ |
+ \[\[
+ [^]]* # Link target
+ \]\]
+ |
+ [\w-]+ # Plain word
+ )
+ (?:\s*
+ , # Sub-vals 1..N
+ \s*
+ (
+ "[^"]*" # Quoted val
+ |
+ \[\[[^]]*\]\] # Link target
+ |
+ [\w-]+ # Plain word
+ )
+ )*
+ )?
+ /x';
+
+ if ( preg_match_all( $regex, $instring, $matches, PREG_SET_ORDER ) ) {
+ foreach ( $matches as $bits ) {
+ array_shift( $bits );
+ $key = strtolower( array_shift( $bits ) );
+ if ( count( $bits ) == 0 ) {
+ $opts[$key] = true;
+ } elseif ( count( $bits ) == 1 ) {
+ $opts[$key] = $this->cleanupOption( array_shift( $bits ) );
+ } else {
+ // Array!
+ $opts[$key] = array_map( array( $this, 'cleanupOption' ), $bits );
+ }
+ }
+ }
+ return $opts;
+ }
+
+ protected function cleanupOption( $opt ) {
+ if ( substr( $opt, 0, 1 ) == '"' ) {
+ return substr( $opt, 1, -1 );
+ }
+
+ if ( substr( $opt, 0, 2 ) == '[[' ) {
+ return substr( $opt, 2, -2 );
+ }
+ return $opt;
+ }
+
+ /**
+ * Use a regex to find out the value of an option
+ * @param $key String: name of option val to retrieve
+ * @param $opts Options array to look in
+ * @param $default Mixed: default value returned if not found
+ */
+ protected static function getOptionValue( $key, $opts, $default ) {
+ $key = strtolower( $key );
+
+ if ( isset( $opts[$key] ) ) {
+ return $opts[$key];
+ } else {
+ return $default;
+ }
+ }
+}
diff --git a/tests/phpunit/includes/parser/ParserHelpers.php b/tests/phpunit/includes/parser/ParserHelpers.php
new file mode 100644
index 00000000..4a6ce7c4
--- /dev/null
+++ b/tests/phpunit/includes/parser/ParserHelpers.php
@@ -0,0 +1,136 @@
+<?php
+
+class PHPUnitParserTest extends ParserTest {
+ function showTesting( $desc ) {
+ /* Do nothing since we don't want to show info during PHPUnit testing. */
+ }
+
+ public function showSuccess( $desc ) {
+ PHPUnit_Framework_Assert::assertTrue( true, $desc );
+ return true;
+ }
+
+ public function showFailure( $desc, $expected, $got ) {
+ PHPUnit_Framework_Assert::assertEquals( $expected, $got, $desc );
+ return false;
+ }
+
+ public function setupRecorder( $options ) {
+ $this->recorder = new PHPUnitTestRecorder( $this );
+ }
+}
+
+class ParserUnitTest extends MediaWikiTestCase {
+ private $test = "";
+
+ public function __construct( $suite, $test = null ) {
+ parent::__construct();
+ $this->test = $test;
+ $this->suite = $suite;
+ }
+
+ function count() { return 1; }
+
+ public function run( PHPUnit_Framework_TestResult $result = null ) {
+ PHPUnit_Framework_Assert::resetCount();
+ if ( $result === NULL ) {
+ $result = new PHPUnit_Framework_TestResult;
+ }
+
+ $this->suite->publishTestArticles(); // Add articles needed by the tests.
+ $backend = new ParserTestSuiteBackend;
+ $result->startTest( $this );
+
+ // Support the transition to PHPUnit 3.5 where PHPUnit_Util_Timer is replaced with PHP_Timer
+ if ( class_exists( 'PHP_Timer' ) ) {
+ PHP_Timer::start();
+ } else {
+ PHPUnit_Util_Timer::start();
+ }
+
+ $r = false;
+ try {
+ # Run the test.
+ # On failure, the subclassed backend will throw an exception with
+ # the details.
+ $pt = new PHPUnitParserTest;
+ $r = $pt->runTest( $this->test['test'], $this->test['input'],
+ $this->test['result'], $this->test['options'], $this->test['config']
+ );
+ }
+ catch ( PHPUnit_Framework_AssertionFailedError $e ) {
+
+ // PHPUnit_Util_Timer -> PHP_Timer support (see above)
+ if ( class_exists( 'PHP_Timer' ) ) {
+ $result->addFailure( $this, $e, PHP_Timer::stop() );
+ } else {
+ $result->addFailure( $this, $e, PHPUnit_Util_Timer::stop() );
+ }
+ }
+ catch ( Exception $e ) {
+ // PHPUnit_Util_Timer -> PHP_Timer support (see above)
+ if ( class_exists( 'PHP_Timer' ) ) {
+ $result->addFailure( $this, $e, PHP_Timer::stop() );
+ } else {
+ $result->addFailure( $this, $e, PHPUnit_Util_Timer::stop() );
+ }
+ }
+
+ // PHPUnit_Util_Timer -> PHP_Timer support (see above)
+ if ( class_exists( 'PHP_Timer' ) ) {
+ $result->endTest( $this, PHP_Timer::stop() );
+ } else {
+ $result->endTest( $this, PHPUnit_Util_Timer::stop() );
+ }
+
+ $backend->recorder->record( $this->test['test'], $r );
+ $this->addToAssertionCount( PHPUnit_Framework_Assert::getCount() );
+
+ return $result;
+ }
+
+ public function toString() {
+ return $this->test['test'];
+ }
+
+}
+
+class ParserTestSuiteBackend extends PHPUnit_FrameWork_TestSuite {
+ public $recorder;
+ public $term;
+ static $usePHPUnit = false;
+
+ function __construct() {
+ parent::__construct();
+ $this->setupRecorder(null);
+ self::$usePHPUnit = method_exists('PHPUnit_Framework_Assert', 'assertEquals');
+ }
+
+ function showTesting( $desc ) {
+ }
+
+ function showRunFile( $path ) {
+ }
+
+ function showTestResult( $desc, $result, $out ) {
+ if ( $result === $out ) {
+ return self::showSuccess( $desc, $result, $out );
+ } else {
+ return self::showFailure( $desc, $result, $out );
+ }
+ }
+
+ public function setupRecorder( $options ) {
+ $this->recorder = new PHPUnitTestRecorder( $this );
+ }
+}
+
+class PHPUnitTestRecorder extends TestRecorder {
+ function record( $test, $result ) {
+ $this->total++;
+ $this->success += $result;
+
+ }
+
+ function reportPercentage( $success, $total ) { }
+}
diff --git a/tests/phpunit/includes/parser/PreprocessorTest.php b/tests/phpunit/includes/parser/PreprocessorTest.php
new file mode 100644
index 00000000..7a5948d4
--- /dev/null
+++ b/tests/phpunit/includes/parser/PreprocessorTest.php
@@ -0,0 +1,195 @@
+<?php
+
+class PreprocessorTest extends MediaWikiTestCase {
+ var $mTitle = 'Page title';
+ var $mPPNodeCount = 0;
+ var $mOptions;
+
+ function setUp() {
+ global $wgParserConf;
+ $this->mOptions = new ParserOptions();
+ $name = isset( $wgParserConf['preprocessorClass'] ) ? $wgParserConf['preprocessorClass'] : 'Preprocessor_DOM';
+
+ $this->mPreprocessor = new $name( $this );
+ }
+
+ function getStripList() {
+ return array( 'gallery', 'display map' /* Used by Maps, see r80025 CR */, '/foo' );
+ }
+
+ function provideCases() {
+ return array(
+ array( "Foo", "<root>Foo</root>" ),
+ array( "<!-- Foo -->", "<root><comment>&lt;!-- Foo --&gt;</comment></root>" ),
+ array( "<!-- Foo --><!-- Bar -->", "<root><comment>&lt;!-- Foo --&gt;</comment><comment>&lt;!-- Bar --&gt;</comment></root>" ),
+ array( "<!-- Foo --> <!-- Bar -->", "<root><comment>&lt;!-- Foo --&gt;</comment> <comment>&lt;!-- Bar --&gt;</comment></root>" ),
+ array( "<!-- Foo --> \n <!-- Bar -->", "<root><comment>&lt;!-- Foo --&gt;</comment> \n <comment>&lt;!-- Bar --&gt;</comment></root>" ),
+ array( "<!-- Foo --> \n <!-- Bar -->\n", "<root><comment>&lt;!-- Foo --&gt;</comment> \n<comment> &lt;!-- Bar --&gt;\n</comment></root>" ),
+ array( "<!-- Foo --> <!-- Bar -->\n", "<root><comment>&lt;!-- Foo --&gt;</comment> <comment>&lt;!-- Bar --&gt;</comment>\n</root>" ),
+ array( "<!-->Bar", "<root><comment>&lt;!--&gt;Bar</comment></root>" ),
+ array( "<!-- Comment -- comment", "<root><comment>&lt;!-- Comment -- comment</comment></root>" ),
+ array( "== Foo ==\n <!-- Bar -->\n== Baz ==\n", "<root><h level=\"2\" i=\"1\">== Foo ==</h>\n<comment> &lt;!-- Bar --&gt;\n</comment><h level=\"2\" i=\"2\">== Baz ==</h>\n</root>" ),
+ array( "<gallery/>", "<root><ext><name>gallery</name><attr></attr></ext></root>" ),
+ array( "Foo <gallery/> Bar", "<root>Foo <ext><name>gallery</name><attr></attr></ext> Bar</root>" ),
+ array( "<gallery></gallery>", "<root><ext><name>gallery</name><attr></attr><inner></inner><close>&lt;/gallery&gt;</close></ext></root>" ),
+ array( "<foo> <gallery></gallery>", "<root>&lt;foo&gt; <ext><name>gallery</name><attr></attr><inner></inner><close>&lt;/gallery&gt;</close></ext></root>" ),
+ array( "<foo> <gallery><gallery></gallery>", "<root>&lt;foo&gt; <ext><name>gallery</name><attr></attr><inner>&lt;gallery&gt;</inner><close>&lt;/gallery&gt;</close></ext></root>" ),
+ array( "<noinclude> Foo bar </noinclude>", "<root><ignore>&lt;noinclude&gt;</ignore> Foo bar <ignore>&lt;/noinclude&gt;</ignore></root>" ),
+ array( "<noinclude>\n{{Foo}}\n</noinclude>", "<root><ignore>&lt;noinclude&gt;</ignore>\n<template lineStart=\"1\"><title>Foo</title></template>\n<ignore>&lt;/noinclude&gt;</ignore></root>" ),
+ array( "<noinclude>\n{{Foo}}\n</noinclude>\n", "<root><ignore>&lt;noinclude&gt;</ignore>\n<template lineStart=\"1\"><title>Foo</title></template>\n<ignore>&lt;/noinclude&gt;</ignore>\n</root>" ),
+ array( "<gallery>foo bar", "<root><ext><name>gallery</name><attr></attr><inner>foo bar</inner></ext></root>" ),
+ array( "<{{foo}}>", "<root>&lt;<template><title>foo</title></template>&gt;</root>" ),
+ array( "<{{{foo}}}>", "<root>&lt;<tplarg><title>foo</title></tplarg>&gt;</root>" ),
+ array( "<gallery></gallery</gallery>", "<root><ext><name>gallery</name><attr></attr><inner>&lt;/gallery</inner><close>&lt;/gallery&gt;</close></ext></root>" ),
+ array( "=== Foo === ", "<root><h level=\"3\" i=\"1\">=== Foo === </h></root>" ),
+ array( "==<!-- -->= Foo === ", "<root><h level=\"2\" i=\"1\">==<comment>&lt;!-- --&gt;</comment>= Foo === </h></root>" ),
+ array( "=== Foo ==<!-- -->= ", "<root><h level=\"1\" i=\"1\">=== Foo ==<comment>&lt;!-- --&gt;</comment>= </h></root>" ),
+ array( "=== Foo ===<!-- -->\n", "<root><h level=\"3\" i=\"1\">=== Foo ===<comment>&lt;!-- --&gt;</comment></h>\n</root>" ),
+ array( "=== Foo ===<!-- --> <!-- -->\n", "<root><h level=\"3\" i=\"1\">=== Foo ===<comment>&lt;!-- --&gt;</comment> <comment>&lt;!-- --&gt;</comment></h>\n</root>" ),
+ array( "== Foo ==\n== Bar == \n", "<root><h level=\"2\" i=\"1\">== Foo ==</h>\n<h level=\"2\" i=\"2\">== Bar == </h>\n</root>" ),
+ array( "===========", "<root><h level=\"5\" i=\"1\">===========</h></root>" ),
+ array( "Foo\n=\n==\n=\n", "<root>Foo\n=\n==\n=\n</root>" ),
+ array( "{{Foo}}", "<root><template lineStart=\"1\"><title>Foo</title></template></root>" ),
+ array( "\n{{Foo}}", "<root>\n<template lineStart=\"1\"><title>Foo</title></template></root>" ),
+ array( "{{Foo|bar}}", "<root><template lineStart=\"1\"><title>Foo</title><part><name index=\"1\" /><value>bar</value></part></template></root>" ),
+ array( "{{Foo|bar}}a", "<root><template lineStart=\"1\"><title>Foo</title><part><name index=\"1\" /><value>bar</value></part></template>a</root>" ),
+ array( "{{Foo|bar|baz}}", "<root><template lineStart=\"1\"><title>Foo</title><part><name index=\"1\" /><value>bar</value></part><part><name index=\"2\" /><value>baz</value></part></template></root>" ),
+ array( "{{Foo|1=bar}}", "<root><template lineStart=\"1\"><title>Foo</title><part><name>1</name>=<value>bar</value></part></template></root>" ),
+ array( "{{Foo|=bar}}", "<root><template lineStart=\"1\"><title>Foo</title><part><name></name>=<value>bar</value></part></template></root>" ),
+ array( "{{Foo|bar=baz}}", "<root><template lineStart=\"1\"><title>Foo</title><part><name>bar</name>=<value>baz</value></part></template></root>" ),
+ array( "{{Foo|1=bar|baz}}", "<root><template lineStart=\"1\"><title>Foo</title><part><name>1</name>=<value>bar</value></part><part><name index=\"1\" /><value>baz</value></part></template></root>" ),
+ array( "{{Foo|1=bar|2=baz}}", "<root><template lineStart=\"1\"><title>Foo</title><part><name>1</name>=<value>bar</value></part><part><name>2</name>=<value>baz</value></part></template></root>" ),
+ array( "{{Foo|bar|foo=baz}}", "<root><template lineStart=\"1\"><title>Foo</title><part><name index=\"1\" /><value>bar</value></part><part><name>foo</name>=<value>baz</value></part></template></root>" ),
+ array( "{{{1}}}", "<root><tplarg lineStart=\"1\"><title>1</title></tplarg></root>" ),
+ array( "{{{1|}}}", "<root><tplarg lineStart=\"1\"><title>1</title><part><name index=\"1\" /><value></value></part></tplarg></root>" ),
+ array( "{{{Foo}}}", "<root><tplarg lineStart=\"1\"><title>Foo</title></tplarg></root>" ),
+ array( "{{{Foo|}}}", "<root><tplarg lineStart=\"1\"><title>Foo</title><part><name index=\"1\" /><value></value></part></tplarg></root>" ),
+ array( "{{{Foo|bar|baz}}}", "<root><tplarg lineStart=\"1\"><title>Foo</title><part><name index=\"1\" /><value>bar</value></part><part><name index=\"2\" /><value>baz</value></part></tplarg></root>" ),
+ array( "{<!-- -->{Foo}}", "<root>{<comment>&lt;!-- --&gt;</comment>{Foo}}</root>" ),
+ array( "{{{{Foobar}}}}", "<root>{<tplarg><title>Foobar</title></tplarg>}</root>" ),
+ array( "{{{ {{Foo}} }}}", "<root><tplarg lineStart=\"1\"><title> <template><title>Foo</title></template> </title></tplarg></root>" ),
+ array( "{{ {{{Foo}}} }}", "<root><template lineStart=\"1\"><title> <tplarg><title>Foo</title></tplarg> </title></template></root>" ),
+ array( "{{{{{Foo}}}}}", "<root><template lineStart=\"1\"><title><tplarg><title>Foo</title></tplarg></title></template></root>" ),
+ array( "{{{{{Foo}} }}}", "<root><tplarg lineStart=\"1\"><title><template><title>Foo</title></template> </title></tplarg></root>" ),
+ array( "{{{{{{Foo}}}}}}", "<root><tplarg lineStart=\"1\"><title><tplarg><title>Foo</title></tplarg></title></tplarg></root>" ),
+ array( "{{{{{{Foo}}}}}", "<root>{<template><title><tplarg><title>Foo</title></tplarg></title></template></root>" ),
+ array( "[[[Foo]]", "<root>[[[Foo]]</root>" ),
+ array( "{{Foo|[[[[bar]]|baz]]}}", "<root><template lineStart=\"1\"><title>Foo</title><part><name index=\"1\" /><value>[[[[bar]]|baz]]</value></part></template></root>" ), // This test is important, since it means the difference between having the [[ rule stacked or not
+ array( "{{Foo|[[[[bar]|baz]]}}", "<root>{{Foo|[[[[bar]|baz]]}}</root>" ),
+ array( "{{Foo|Foo [[[[bar]|baz]]}}", "<root>{{Foo|Foo [[[[bar]|baz]]}}</root>" ),
+ array( "Foo <display map>Bar</display map >Baz", "<root>Foo <ext><name>display map</name><attr></attr><inner>Bar</inner><close>&lt;/display map &gt;</close></ext>Baz</root>" ),
+ array( "Foo <display map foo>Bar</display map >Baz", "<root>Foo <ext><name>display map</name><attr> foo</attr><inner>Bar</inner><close>&lt;/display map &gt;</close></ext>Baz</root>" ),
+ array( "Foo <gallery bar=\"baz\" />", "<root>Foo <ext><name>gallery</name><attr> bar=&quot;baz&quot; </attr></ext></root>" ),
+ array( "</foo>Foo<//foo>", "<root><ext><name>/foo</name><attr></attr><inner>Foo</inner><close>&lt;//foo&gt;</close></ext></root>" ), # Worth blacklisting IMHO
+ array( "{{#ifexpr: ({{{1|1}}} = 2) | Foo | Bar }}", "<root><template lineStart=\"1\"><title>#ifexpr: (<tplarg><title>1</title><part><name index=\"1\" /><value>1</value></part></tplarg> = 2) </title><part><name index=\"1\" /><value> Foo </value></part><part><name index=\"2\" /><value> Bar </value></part></template></root>"),
+ array( "{{#if: {{{1|}}} | Foo | {{Bar}} }}", "<root><template lineStart=\"1\"><title>#if: <tplarg><title>1</title><part><name index=\"1\" /><value></value></part></tplarg> </title><part><name index=\"1\" /><value> Foo </value></part><part><name index=\"2\" /><value> <template><title>Bar</title></template> </value></part></template></root>"),
+ array( "{{#if: {{{1|}}} | Foo | [[Bar]] }}", "<root><template lineStart=\"1\"><title>#if: <tplarg><title>1</title><part><name index=\"1\" /><value></value></part></tplarg> </title><part><name index=\"1\" /><value> Foo </value></part><part><name index=\"2\" /><value> [[Bar]] </value></part></template></root>"),
+ array( "{{#if: {{{1|}}} | [[Foo]] | Bar }}", "<root><template lineStart=\"1\"><title>#if: <tplarg><title>1</title><part><name index=\"1\" /><value></value></part></tplarg> </title><part><name index=\"1\" /><value> [[Foo]] </value></part><part><name index=\"2\" /><value> Bar </value></part></template></root>"),
+ array( "{{#if: {{{1|}}} | 1 | {{#if: {{{1|}}} | 2 | 3 }} }}", "<root><template lineStart=\"1\"><title>#if: <tplarg><title>1</title><part><name index=\"1\" /><value></value></part></tplarg> </title><part><name index=\"1\" /><value> 1 </value></part><part><name index=\"2\" /><value> <template><title>#if: <tplarg><title>1</title><part><name index=\"1\" /><value></value></part></tplarg> </title><part><name index=\"1\" /><value> 2 </value></part><part><name index=\"2\" /><value> 3 </value></part></template> </value></part></template></root>"),
+ array( "{{ {{Foo}}", "<root>{{ <template><title>Foo</title></template></root>"),
+ array( "{{Foobar {{Foo}} {{Bar}} {{Baz}} ", "<root>{{Foobar <template><title>Foo</title></template> <template><title>Bar</title></template> <template><title>Baz</title></template> </root>"),
+ array( "[[Foo]] |", "<root>[[Foo]] |</root>"),
+ array( "{{Foo|Bar|", "<root>{{Foo|Bar|</root>"),
+ array( "[[Foo]", "<root>[[Foo]</root>"),
+ array( "[[Foo|Bar]", "<root>[[Foo|Bar]</root>"),
+ array( "{{Foo| [[Bar] }}", "<root>{{Foo| [[Bar] }}</root>"),
+ array( "{{Foo| [[Bar|Baz] }}", "<root>{{Foo| [[Bar|Baz] }}</root>"),
+ array( "{{Foo|bar=[[baz]}}", "<root>{{Foo|bar=[[baz]}}</root>"),
+ array( "{{foo|", "<root>{{foo|</root>"),
+ array( "{{foo|}", "<root>{{foo|}</root>"),
+ array( "{{foo|} }}", "<root><template lineStart=\"1\"><title>foo</title><part><name index=\"1\" /><value>} </value></part></template></root>"),
+ array( "{{foo|bar=|}", "<root>{{foo|bar=|}</root>"),
+ array( "{{Foo|} Bar=", "<root>{{Foo|} Bar=</root>"),
+ array( "{{Foo|} Bar=}}", "<root><template lineStart=\"1\"><title>Foo</title><part><name>} Bar</name>=<value></value></part></template></root>"),
+ /* array( file_get_contents( dirname( __FILE__ ) . '/QuoteQuran.txt' ), file_get_contents( dirname( __FILE__ ) . '/QuoteQuranExpanded.txt' ) ), */
+ );
+ }
+
+ /**
+ * @dataProvider provideCases
+ */
+ function testPreprocessorOutput( $wikiText, $expectedXml ) {
+ $this->assertEquals( $expectedXml, $this->mPreprocessor->preprocessToXml( $wikiText ) );
+ }
+
+ /**
+ * These are more complex test cases taken out of wiki articles.
+ */
+ function provideFiles() {
+ return array(
+ array( "QuoteQuran" ), # http://en.wikipedia.org/w/index.php?title=Template:QuoteQuran/sandbox&oldid=237348988 GFDL + CC-BY-SA by Striver
+ array( "Factorial" ), # http://en.wikipedia.org/w/index.php?title=Template:Factorial&oldid=98548758 GFDL + CC-BY-SA by Polonium
+ array( "All_system_messages" ), # http://tl.wiktionary.org/w/index.php?title=Suleras:All_system_messages&oldid=2765 GPL text generated by MediaWiki
+ array( "Fundraising" ), # http://tl.wiktionary.org/w/index.php?title=MediaWiki:Sitenotice&oldid=5716 GFDL + CC-BY-SA, copied there by Sky Harbor.
+ );
+ }
+
+ /**
+ * @dataProvider provideFiles
+ */
+ function testPreprocessorOutputFiles( $filename ) {
+ $folder = dirname( __FILE__ ) . "/../../../parser/preprocess";
+ $wikiText = file_get_contents( "$folder/$filename.txt" );
+ $output = $this->mPreprocessor->preprocessToXml( $wikiText );
+
+ $expectedFilename = "$folder/$filename.expected";
+ if ( file_exists( $expectedFilename ) ) {
+ $this->assertStringEqualsFile( $expectedFilename, $output );
+ } else {
+ $tempFilename = tempnam( $folder, "$filename." );
+ file_put_contents( $tempFilename, $output );
+ $this->markTestIncomplete( "File $expectedFilename missing. Output stored as $tempFilename" );
+ }
+ }
+
+ /**
+ * Tests from Bug 28642 · https://bugzilla.wikimedia.org/28642
+ */
+ function provideHeadings() {
+ return array( /* These should become headings: */
+ array( "== h ==<!--c1-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment></h></root>" ),
+ array( "== h == <!--c1-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment></h></root>" ),
+ array( "== h ==<!--c1--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> </h></root>" ),
+ array( "== h == <!--c1--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> </h></root>" ),
+ array( "== h ==<!--c1--><!--c2-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment></h></root>" ),
+ array( "== h == <!--c1--><!--c2-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment></h></root>" ),
+ array( "== h ==<!--c1--><!--c2--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment> </h></root>" ),
+ array( "== h == <!--c1--><!--c2--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment> </h></root>" ),
+ array( "== h == <!--c1--> <!--c2-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment></h></root>" ),
+ array( "== h ==<!--c1--> <!--c2--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> </h></root>" ),
+ array( "== h == <!--c1--> <!--c2--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> </h></root>" ),
+ array( "== h ==<!--c1--><!--c2--><!--c3-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment></h></root>" ),
+ array( "== h ==<!--c1--> <!--c2--><!--c3-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment></h></root>" ),
+ array( "== h ==<!--c1--><!--c2--> <!--c3-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment></h></root>" ),
+ array( "== h ==<!--c1--> <!--c2--> <!--c3-->", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment></h></root>" ),
+ array( "== h == <!--c1--><!--c2--><!--c3-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment></h></root>" ),
+ array( "== h == <!--c1--> <!--c2--><!--c3-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment></h></root>" ),
+ array( "== h == <!--c1--><!--c2--> <!--c3-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment></h></root>" ),
+ array( "== h == <!--c1--> <!--c2--> <!--c3-->", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment></h></root>" ),
+ array( "== h ==<!--c1--><!--c2--><!--c3--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> </h></root>" ),
+ array( "== h ==<!--c1--> <!--c2--><!--c3--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> </h></root>" ),
+ array( "== h ==<!--c1--><!--c2--> <!--c3--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment> </h></root>" ),
+ array( "== h ==<!--c1--> <!--c2--> <!--c3--> ", "<root><h level=\"2\" i=\"1\">== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment> </h></root>" ),
+ array( "== h == <!--c1--><!--c2--><!--c3--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> </h></root>" ),
+ array( "== h == <!--c1--> <!--c2--><!--c3--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> </h></root>" ),
+ array( "== h == <!--c1--><!--c2--> <!--c3--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment> </h></root>" ),
+ array( "== h == <!--c1--> <!--c2--> <!--c3--> ", "<root><h level=\"2\" i=\"1\">== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> <comment>&lt;!--c3--&gt;</comment> </h></root>" ),
+
+ /* These are not working: */
+ array( "== h ==<!--c1--> <!--c2-->", "<root>== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment></root>" ),
+ array( "== h == <!--c1--> <!--c2-->", "<root>== h == <comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment></root>" ),
+ array( "== h ==<!--c1--> <!--c2--> ", "<root>== h ==<comment>&lt;!--c1--&gt;</comment> <comment>&lt;!--c2--&gt;</comment> </root>" ),
+ array( "== h == x <!--c1--><!--c2--><!--c3--> ", "<root>== h == x <comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> </root>" ),
+ array( "== h ==<!--c1--> x <!--c2--><!--c3--> ", "<root>== h ==<comment>&lt;!--c1--&gt;</comment> x <comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> </root>" ),
+ array( "== h ==<!--c1--><!--c2--><!--c3--> x ", "<root>== h ==<comment>&lt;!--c1--&gt;</comment><comment>&lt;!--c2--&gt;</comment><comment>&lt;!--c3--&gt;</comment> x </root>" ),
+ );
+ }
+
+ /**
+ * @dataProvider provideHeadings
+ */
+ function testHeadings( $wikiText, $expectedXml ) {
+ $this->assertEquals( $expectedXml, $this->mPreprocessor->preprocessToXml( $wikiText ) );
+ }
+}
+
diff --git a/tests/phpunit/includes/parser/TagHooks.php b/tests/phpunit/includes/parser/TagHooks.php
new file mode 100644
index 00000000..713ce846
--- /dev/null
+++ b/tests/phpunit/includes/parser/TagHooks.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * @group Parser
+ */
+class TagHookTest extends MediaWikiTestCase {
+
+ public static function provideValidNames() {
+ return array( array( 'foo' ), array( 'foo-bar' ), array( 'foo_bar' ), array( 'FOO-BAR' ), array( 'foo bar' ) );
+ }
+
+ public static function provideBadNames() {
+ return array( array( "foo<bar" ), array( "foo>bar" ), array( "foo\nbar" ), array( "foo\rbar" ) );
+ }
+
+ /**
+ * @dataProvider provideValidNames
+ */
+ function testTagHooks( $tag ) {
+ global $wgParserConf;
+ $parser = new Parser( $wgParserConf );
+
+ $parser->setHook( $tag, array( $this, 'tagCallback' ) );
+ $parserOutput = $parser->parse( "Foo<$tag>Bar</$tag>Baz", Title::newFromText( 'Test' ), new ParserOptions );
+ $this->assertEquals( "<p>FooOneBaz\n</p>", $parserOutput->getText() );
+
+ $parser->mPreprocessor = null; # Break the Parser <-> Preprocessor cycle
+ }
+
+ /**
+ * @dataProvider provideBadNames
+ * @expectedException MWException
+ */
+ function testBadTagHooks( $tag ) {
+ global $wgParserConf;
+ $parser = new Parser( $wgParserConf );
+
+ $parser->setHook( $tag, array( $this, 'tagCallback' ) );
+ $parser->parse( "Foo<$tag>Bar</$tag>Baz", Title::newFromText( 'Test' ), new ParserOptions );
+ $this->fail('Exception not thrown.');
+ }
+
+ /**
+ * @dataProvider provideValidNames
+ */
+ function testFunctionTagHooks( $tag ) {
+ global $wgParserConf;
+ $parser = new Parser( $wgParserConf );
+
+ $parser->setFunctionTagHook( $tag, array( $this, 'functionTagCallback' ), 0 );
+ $parserOutput = $parser->parse( "Foo<$tag>Bar</$tag>Baz", Title::newFromText( 'Test' ), new ParserOptions );
+ $this->assertEquals( "<p>FooOneBaz\n</p>", $parserOutput->getText() );
+
+ $parser->mPreprocessor = null; # Break the Parser <-> Preprocessor cycle
+ }
+
+ /**
+ * @dataProvider provideBadNames
+ * @expectedException MWException
+ */
+ function testBadFunctionTagHooks( $tag ) {
+ global $wgParserConf;
+ $parser = new Parser( $wgParserConf );
+
+ $parser->setFunctionTagHook( $tag, array( $this, 'functionTagCallback' ), SFH_OBJECT_ARGS );
+ $parser->parse( "Foo<$tag>Bar</$tag>Baz", Title::newFromText( 'Test' ), new ParserOptions );
+ $this->fail('Exception not thrown.');
+ }
+
+ function tagCallback( $text, $params, $parser ) {
+ return str_rot13( $text );
+ }
+
+ function functionTagCallback( &$parser, $frame, $code, $attribs ) {
+ return str_rot13( $code );
+ }
+}
diff --git a/tests/phpunit/includes/search/SearchEngineTest.php b/tests/phpunit/includes/search/SearchEngineTest.php
new file mode 100644
index 00000000..1a0fcd31
--- /dev/null
+++ b/tests/phpunit/includes/search/SearchEngineTest.php
@@ -0,0 +1,163 @@
+<?php
+
+/**
+ * This class is not directly tested. Instead it is extended by SearchDbTest.
+ * @group Search
+ * @group Database
+ */
+class SearchEngineTest extends MediaWikiTestCase {
+ protected $search, $pageList;
+
+ function tearDown() {
+ unset( $this->search );
+ }
+
+ /*
+ * Checks for database type & version.
+ * Will skip current test if DB does not support search.
+ */
+ function setUp() {
+ parent::setUp();
+ // Search tests require MySQL or SQLite with FTS
+ # Get database type and version
+ $dbType = $this->db->getType();
+ $dbSupported =
+ ($dbType === 'mysql')
+ || ( $dbType === 'sqlite' && $this->db->getFulltextSearchModule() == 'FTS3' );
+
+ if( !$dbSupported ) {
+ $this->markTestSkipped( "MySQL or SQLite with FTS3 only" );
+ }
+
+ $searchType = $this->db->getSearchEngine();
+ $this->search = new $searchType( $this->db );
+ }
+
+ function pageExists( $title ) {
+ return false;
+ }
+
+ function addDBData() {
+ if ( $this->pageExists( 'Not_Main_Page' ) ) {
+ return;
+ }
+ $this->insertPage( "Not_Main_Page", "This is not a main page", 0 );
+ $this->insertPage( 'Talk:Not_Main_Page', 'This is not a talk page to the main page, see [[smithee]]', 1 );
+ $this->insertPage( 'Smithee', 'A smithee is one who smiths. See also [[Alan Smithee]]', 0 );
+ $this->insertPage( 'Talk:Smithee', 'This article sucks.', 1 );
+ $this->insertPage( 'Unrelated_page', 'Nothing in this page is about the S word.', 0 );
+ $this->insertPage( 'Another_page', 'This page also is unrelated.', 0 );
+ $this->insertPage( 'Help:Help', 'Help me!', 4 );
+ $this->insertPage( 'Thppt', 'Blah blah', 0 );
+ $this->insertPage( 'Alan_Smithee', 'yum', 0 );
+ $this->insertPage( 'Pages', 'are\'food', 0 );
+ $this->insertPage( 'HalfOneUp', 'AZ', 0 );
+ $this->insertPage( 'FullOneUp', 'AZ', 0 );
+ $this->insertPage( 'HalfTwoLow', 'az', 0 );
+ $this->insertPage( 'FullTwoLow', 'ï½ï½š', 0 );
+ $this->insertPage( 'HalfNumbers', '1234567890', 0 );
+ $this->insertPage( 'FullNumbers', '123456789ï¼', 0 );
+ $this->insertPage( 'DomainName', 'example.com', 0 );
+ }
+
+ function fetchIds( $results ) {
+ $this->assertTrue( is_object( $results ) );
+
+ $matches = array();
+ while ( $row = $results->next() ) {
+ $matches[] = $row->getTitle()->getPrefixedText();
+ }
+ $results->free();
+ # Search is not guaranteed to return results in a certain order;
+ # sort them numerically so we will compare simply that we received
+ # the expected matches.
+ sort( $matches );
+ return $matches;
+ }
+
+ /**
+ * Insert a new page
+ *
+ * @param $pageName String: page name
+ * @param $text String: page's content
+ * @param $n Integer: unused
+ */
+ function insertPage( $pageName, $text, $ns ) {
+ $dbw = $this->db;
+ $title = Title::newFromText( $pageName );
+
+ $user = User::newFromName( 'WikiSysop' );
+ $comment = 'Search Test';
+
+ // avoid memory leak...?
+ $linkCache = LinkCache::singleton();
+ $linkCache->clear();
+
+ $article = new Article( $title );
+ $article->doEdit( $text, $comment, 0, false, $user );
+
+ $this->pageList[] = array( $title, $article->getId() );
+
+ return true;
+ }
+
+ function testFullWidth() {
+ $this->assertEquals(
+ array( 'FullOneUp', 'FullTwoLow', 'HalfOneUp', 'HalfTwoLow' ),
+ $this->fetchIds( $this->search->searchText( 'AZ' ) ),
+ "Search for normalized from Half-width Upper" );
+ $this->assertEquals(
+ array( 'FullOneUp', 'FullTwoLow', 'HalfOneUp', 'HalfTwoLow' ),
+ $this->fetchIds( $this->search->searchText( 'az' ) ),
+ "Search for normalized from Half-width Lower" );
+ $this->assertEquals(
+ array( 'FullOneUp', 'FullTwoLow', 'HalfOneUp', 'HalfTwoLow' ),
+ $this->fetchIds( $this->search->searchText( 'AZ' ) ),
+ "Search for normalized from Full-width Upper" );
+ $this->assertEquals(
+ array( 'FullOneUp', 'FullTwoLow', 'HalfOneUp', 'HalfTwoLow' ),
+ $this->fetchIds( $this->search->searchText( 'ï½ï½š' ) ),
+ "Search for normalized from Full-width Lower" );
+ }
+
+ function testTextSearch() {
+ $this->assertEquals(
+ array( 'Smithee' ),
+ $this->fetchIds( $this->search->searchText( 'smithee' ) ),
+ "Plain search failed" );
+ }
+
+ function testTextPowerSearch() {
+ $this->search->setNamespaces( array( 0, 1, 4 ) );
+ $this->assertEquals(
+ array(
+ 'Smithee',
+ 'Talk:Not Main Page',
+ ),
+ $this->fetchIds( $this->search->searchText( 'smithee' ) ),
+ "Power search failed" );
+ }
+
+ function testTitleSearch() {
+ $this->assertEquals(
+ array(
+ 'Alan Smithee',
+ 'Smithee',
+ ),
+ $this->fetchIds( $this->search->searchTitle( 'smithee' ) ),
+ "Title search failed" );
+ }
+
+ function testTextTitlePowerSearch() {
+ $this->search->setNamespaces( array( 0, 1, 4 ) );
+ $this->assertEquals(
+ array(
+ 'Alan Smithee',
+ 'Smithee',
+ 'Talk:Smithee',
+ ),
+ $this->fetchIds( $this->search->searchTitle( 'smithee' ) ),
+ "Title power search failed" );
+ }
+
+}
diff --git a/tests/phpunit/includes/search/SearchUpdateTest.php b/tests/phpunit/includes/search/SearchUpdateTest.php
new file mode 100644
index 00000000..935425a6
--- /dev/null
+++ b/tests/phpunit/includes/search/SearchUpdateTest.php
@@ -0,0 +1,80 @@
+<?php
+
+class MockSearch extends SearchEngine {
+ public static $id;
+ public static $title;
+ public static $text;
+
+ public function __construct( $db ) {
+ }
+
+ public function update( $id, $title, $text ) {
+ self::$id = $id;
+ self::$title = $title;
+ self::$text = $text;
+ }
+}
+
+/**
+ * @group Search
+ */
+class SearchUpdateTest extends MediaWikiTestCase {
+ static $searchType;
+
+ function update( $text, $title = 'Test', $id = 1 ) {
+ $u = new SearchUpdate( $id, $title, $text );
+ $u->doUpdate();
+ return array( MockSearch::$title, MockSearch::$text );
+ }
+
+ function updateText( $text ) {
+ list( , $resultText ) = $this->update( $text );
+ $resultText = trim( $resultText ); // abstract from some implementation details
+ return $resultText;
+ }
+
+ function setUp() {
+ global $wgSearchType;
+
+ self::$searchType = $wgSearchType;
+ $wgSearchType = 'MockSearch';
+ }
+
+ function tearDown() {
+ global $wgSearchType;
+
+ $wgSearchType = self::$searchType;
+ }
+
+ function testUpdateText() {
+ $this->assertEquals(
+ 'test',
+ $this->updateText( '<div>TeSt</div>' ),
+ 'HTML stripped, text lowercased'
+ );
+
+ $this->assertEquals(
+ 'foo bar boz quux',
+ $this->updateText( <<<EOT
+<table style="color:red; font-size:100px">
+ <tr class="scary"><td><div>foo</div></td><tr>bar</td></tr>
+ <tr><td>boz</td><tr>quux</td></tr>
+</table>
+EOT
+ ), 'Stripping HTML tables' );
+
+ $this->assertEquals(
+ 'a b',
+ $this->updateText( 'a > b' ),
+ 'Handle unclosed tags'
+ );
+
+ $text = str_pad( "foo <barbarbar \n", 10000, 'x' );
+
+ $this->assertNotEquals(
+ '',
+ $this->updateText( $text ),
+ 'Bug 18609'
+ );
+ }
+}
diff --git a/tests/phpunit/includes/specials/SpecialRecentchanges.php b/tests/phpunit/includes/specials/SpecialRecentchanges.php
new file mode 100644
index 00000000..a98e7c1a
--- /dev/null
+++ b/tests/phpunit/includes/specials/SpecialRecentchanges.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Test class for SpecialRecentchanges class
+ *
+ * Copyright © 2011, Ashar Voultoiz
+ *
+ * @author Ashar Voultoiz
+ */
+class SpecialRecentchangesTest extends MediaWikiTestCase {
+
+ /**
+ * @var SpecialRecentChanges
+ */
+ protected $rc;
+
+ function setUp() {
+ }
+
+ /** helper to test SpecialRecentchanges::buildMainQueryConds() */
+ private function assertConditions( $expected, $requestOptions = null, $message = '' ) {
+ global $wgRequest;
+ $savedGlobal = $wgRequest;
+
+ # Initialize a WebRequest object ...
+ $wgRequest = new FauxRequest( $requestOptions );
+ # ... then setup the rc object (which use wgRequest internally)
+ $this->rc = new SpecialRecentChanges();
+ $formOptions = $this->rc->setup( null );
+
+ # Filter out rc_timestamp conditions which depends on the test runtime
+ # This condition is not needed as of march 2, 2011 -- hashar
+ # @todo FIXME: Find a way to generate the correct rc_timestamp
+ $queryConditions = array_filter(
+ $this->rc->buildMainQueryConds( $formOptions ),
+ 'SpecialRecentchangesTest::filterOutRcTimestampCondition'
+ );
+
+ $this->assertEquals(
+ $expected,
+ $queryConditions,
+ $message
+ );
+
+ $wgRequest = $savedGlobal;
+ }
+
+ /** return false if condition begin with 'rc_timestamp ' */
+ private static function filterOutRcTimestampCondition( $var ) {
+ return (false === strpos( $var, 'rc_timestamp ' ));
+
+ }
+
+ public function testRcNsFilter() {
+ $this->assertConditions(
+ array( # expected
+ 'rc_bot' => 0,
+ #0 => "rc_timestamp >= '20110223000000'",
+ 1 => "rc_namespace = '0'",
+ ),
+ array(
+ 'namespace' => NS_MAIN,
+ ),
+ "rc conditions with no options (aka default setting)"
+ );
+ }
+
+ public function testRcNsFilterInversion() {
+ $this->assertConditions(
+ array( # expected
+ #0 => "rc_timestamp >= '20110223000000'",
+ 'rc_bot' => 0,
+ 1 => sprintf( "rc_namespace != '%s'", NS_MAIN ),
+ ),
+ array(
+ 'namespace' => NS_MAIN,
+ 'invert' => 1,
+ ),
+ "rc conditions with namespace inverted"
+ );
+ }
+
+ /**
+ * @bug 2429
+ * @dataProvider provideNamespacesAssociations
+ */
+ public function testRcNsFilterAssociation( $ns1, $ns2 ) {
+ $this->assertConditions(
+ array( # expected
+ #0 => "rc_timestamp >= '20110223000000'",
+ 'rc_bot' => 0,
+ 1 => sprintf( "(rc_namespace = '%s' OR rc_namespace = '%s')", $ns1, $ns2 ),
+ ),
+ array(
+ 'namespace' => $ns1,
+ 'associated' => 1,
+ ),
+ "rc conditions with namespace inverted"
+ );
+ }
+
+ /**
+ * @bug 2429
+ * @dataProvider provideNamespacesAssociations
+ */
+ public function testRcNsFilterAssociationWithInversion( $ns1, $ns2 ) {
+ $this->assertConditions(
+ array( # expected
+ #0 => "rc_timestamp >= '20110223000000'",
+ 'rc_bot' => 0,
+ 1 => sprintf( "(rc_namespace != '%s' AND rc_namespace != '%s')", $ns1, $ns2 ),
+ ),
+ array(
+ 'namespace' => $ns1,
+ 'associated' => 1,
+ 'invert' => 1,
+ ),
+ "rc conditions with namespace inverted"
+ );
+ }
+
+ /**
+ * Provides associated namespaces to test recent changes
+ * namespaces association filtering.
+ */
+ public function provideNamespacesAssociations() {
+ return array( # (NS => Associated_NS)
+ array( NS_MAIN, NS_TALK),
+ array( NS_TALK, NS_MAIN),
+ );
+ }
+
+}
+
+
diff --git a/tests/phpunit/includes/upload/UploadFromUrlTest.php b/tests/phpunit/includes/upload/UploadFromUrlTest.php
new file mode 100644
index 00000000..4722d408
--- /dev/null
+++ b/tests/phpunit/includes/upload/UploadFromUrlTest.php
@@ -0,0 +1,348 @@
+<?php
+
+/**
+ * @group Broken
+ * @group Upload
+ */
+class UploadFromUrlTest extends ApiTestCase {
+
+ public function setUp() {
+ global $wgEnableUploads, $wgAllowCopyUploads, $wgAllowAsyncCopyUploads;
+ parent::setUp();
+
+ $wgEnableUploads = true;
+ $wgAllowCopyUploads = true;
+ $wgAllowAsyncCopyUploads = true;
+ wfSetupSession();
+
+ if ( wfLocalFile( 'UploadFromUrlTest.png' )->exists() ) {
+ $this->deleteFile( 'UploadFromUrlTest.png' );
+ }
+ }
+
+ protected function doApiRequest( $params, $unused = null, $appendModule = false ) {
+ $sessionId = session_id();
+ session_write_close();
+
+ $req = new FauxRequest( $params, true, $_SESSION );
+ $module = new ApiMain( $req, true );
+ $module->execute();
+
+ wfSetupSession( $sessionId );
+ return array( $module->getResultData(), $req );
+ }
+
+ /**
+ * Ensure that the job queue is empty before continuing
+ */
+ public function testClearQueue() {
+ while ( $job = Job::pop() ) { }
+ $this->assertFalse( $job );
+ }
+
+ /**
+ * @todo Document why we test login, since the $wgUser hack used doesn't
+ * require login
+ */
+ public function testLogin() {
+ $data = $this->doApiRequest( array(
+ 'action' => 'login',
+ 'lgname' => $this->user->userName,
+ 'lgpassword' => $this->user->passWord ) );
+ $this->assertArrayHasKey( "login", $data[0] );
+ $this->assertArrayHasKey( "result", $data[0]['login'] );
+ $this->assertEquals( "NeedToken", $data[0]['login']['result'] );
+ $token = $data[0]['login']['token'];
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'login',
+ "lgtoken" => $token,
+ 'lgname' => $this->user->userName,
+ 'lgpassword' => $this->user->passWord ) );
+
+ $this->assertArrayHasKey( "login", $data[0] );
+ $this->assertArrayHasKey( "result", $data[0]['login'] );
+ $this->assertEquals( "Success", $data[0]['login']['result'] );
+ $this->assertArrayHasKey( 'lgtoken', $data[0]['login'] );
+
+ return $data;
+ }
+
+ /**
+ * @depends testLogin
+ * @depends testClearQueue
+ */
+ public function testSetupUrlDownload( $data ) {
+ $token = $this->user->editToken();
+ $exception = false;
+
+ try {
+ $this->doApiRequest( array(
+ 'action' => 'upload',
+ ) );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ $this->assertEquals( "The token parameter must be set", $e->getMessage() );
+ }
+ $this->assertTrue( $exception, "Got exception" );
+
+ $exception = false;
+ try {
+ $this->doApiRequest( array(
+ 'action' => 'upload',
+ 'token' => $token,
+ ), $data );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ $this->assertEquals( "One of the parameters sessionkey, file, url, statuskey is required",
+ $e->getMessage() );
+ }
+ $this->assertTrue( $exception, "Got exception" );
+
+ $exception = false;
+ try {
+ $this->doApiRequest( array(
+ 'action' => 'upload',
+ 'url' => 'http://www.example.com/test.png',
+ 'token' => $token,
+ ), $data );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ $this->assertEquals( "The filename parameter must be set", $e->getMessage() );
+ }
+ $this->assertTrue( $exception, "Got exception" );
+
+ $this->user->removeGroup( 'sysop' );
+ $exception = false;
+ try {
+ $this->doApiRequest( array(
+ 'action' => 'upload',
+ 'url' => 'http://www.example.com/test.png',
+ 'filename' => 'UploadFromUrlTest.png',
+ 'token' => $token,
+ ), $data );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ $this->assertEquals( "Permission denied", $e->getMessage() );
+ }
+ $this->assertTrue( $exception, "Got exception" );
+
+ $this->user->addGroup( 'sysop' );
+ $data = $this->doApiRequest( array(
+ 'action' => 'upload',
+ 'url' => 'http://bits.wikimedia.org/skins-1.5/common/images/poweredby_mediawiki_88x31.png',
+ 'asyncdownload' => 1,
+ 'filename' => 'UploadFromUrlTest.png',
+ 'token' => $token,
+ ), $data );
+
+ $this->assertEquals( $data[0]['upload']['result'], 'Queued', 'Queued upload' );
+
+ $job = Job::pop();
+ $this->assertThat( $job, $this->isInstanceOf( 'UploadFromUrlJob' ), 'Queued upload inserted' );
+ }
+
+ /**
+ * @depends testLogin
+ * @depends testClearQueue
+ */
+ public function testAsyncUpload( $data ) {
+ $token = $this->user->editToken();
+
+ $this->user->addGroup( 'users' );
+
+ $data = $this->doAsyncUpload( $token, true );
+ $this->assertEquals( $data[0]['upload']['result'], 'Success' );
+ $this->assertEquals( $data[0]['upload']['filename'], 'UploadFromUrlTest.png' );
+ $this->assertTrue( wfLocalFile( $data[0]['upload']['filename'] )->exists() );
+
+ $this->deleteFile( 'UploadFromUrlTest.png' );
+
+ return $data;
+ }
+
+ /**
+ * @depends testLogin
+ * @depends testClearQueue
+ */
+ public function testAsyncUploadWarning( $data ) {
+ $token = $this->user->editToken();
+
+ $this->user->addGroup( 'users' );
+
+
+ $data = $this->doAsyncUpload( $token );
+
+ $this->assertEquals( $data[0]['upload']['result'], 'Warning' );
+ $this->assertTrue( isset( $data[0]['upload']['sessionkey'] ) );
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'upload',
+ 'sessionkey' => $data[0]['upload']['sessionkey'],
+ 'filename' => 'UploadFromUrlTest.png',
+ 'ignorewarnings' => 1,
+ 'token' => $token,
+ ) );
+ $this->assertEquals( $data[0]['upload']['result'], 'Success' );
+ $this->assertEquals( $data[0]['upload']['filename'], 'UploadFromUrlTest.png' );
+ $this->assertTrue( wfLocalFile( $data[0]['upload']['filename'] )->exists() );
+
+ $this->deleteFile( 'UploadFromUrlTest.png' );
+
+ return $data;
+ }
+
+ /**
+ * @depends testLogin
+ * @depends testClearQueue
+ */
+ public function testSyncDownload( $data ) {
+ $token = $this->user->editToken();
+
+ $job = Job::pop();
+ $this->assertFalse( $job, 'Starting with an empty jobqueue' );
+
+ $this->user->addGroup( 'users' );
+ $data = $this->doApiRequest( array(
+ 'action' => 'upload',
+ 'filename' => 'UploadFromUrlTest.png',
+ 'url' => 'http://bits.wikimedia.org/skins-1.5/common/images/poweredby_mediawiki_88x31.png',
+ 'ignorewarnings' => true,
+ 'token' => $token,
+ ), $data );
+
+ $job = Job::pop();
+ $this->assertFalse( $job );
+
+ $this->assertEquals( 'Success', $data[0]['upload']['result'] );
+ $this->deleteFile( 'UploadFromUrlTest.png' );
+
+ return $data;
+ }
+
+ public function testLeaveMessage() {
+ $token = $this->user->user->editToken();
+
+ $talk = $this->user->user->getTalkPage();
+ if ( $talk->exists() ) {
+ $a = new Article( $talk );
+ $a->doDeleteArticle( '' );
+ }
+
+ $this->assertFalse( (bool)$talk->getArticleId( Title::GAID_FOR_UPDATE ), 'User talk does not exist' );
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'upload',
+ 'filename' => 'UploadFromUrlTest.png',
+ 'url' => 'http://bits.wikimedia.org/skins-1.5/common/images/poweredby_mediawiki_88x31.png',
+ 'asyncdownload' => 1,
+ 'token' => $token,
+ 'leavemessage' => 1,
+ 'ignorewarnings' => 1,
+ ) );
+
+ $job = Job::pop();
+ $this->assertEquals( 'UploadFromUrlJob', get_class( $job ) );
+ $job->run();
+
+ $this->assertTrue( wfLocalFile( 'UploadFromUrlTest.png' )->exists() );
+ $this->assertTrue( (bool)$talk->getArticleId( Title::GAID_FOR_UPDATE ), 'User talk exists' );
+
+ $this->deleteFile( 'UploadFromUrlTest.png' );
+
+ $talkRev = Revision::newFromTitle( $talk );
+ $talkSize = $talkRev->getSize();
+
+ $exception = false;
+ try {
+ $data = $this->doApiRequest( array(
+ 'action' => 'upload',
+ 'filename' => 'UploadFromUrlTest.png',
+ 'url' => 'http://bits.wikimedia.org/skins-1.5/common/images/poweredby_mediawiki_88x31.png',
+ 'asyncdownload' => 1,
+ 'token' => $token,
+ 'leavemessage' => 1,
+ ) );
+ } catch ( UsageException $e ) {
+ $exception = true;
+ $this->assertEquals( 'Using leavemessage without ignorewarnings is not supported', $e->getMessage() );
+ }
+ $this->assertTrue( $exception );
+
+ $job = Job::pop();
+ $this->assertFalse( $job );
+
+ return;
+
+ /**
+ // Broken until using leavemessage with ignorewarnings is supported
+ $job->run();
+
+ $this->assertFalse( wfLocalFile( 'UploadFromUrlTest.png' )->exists() );
+
+ $talkRev = Revision::newFromTitle( $talk );
+ $this->assertTrue( $talkRev->getSize() > $talkSize, 'New message left' );
+ */
+ }
+
+ /**
+ * Helper function to perform an async upload, execute the job and fetch
+ * the status
+ *
+ * @return array The result of action=upload&statuskey=key
+ */
+ private function doAsyncUpload( $token, $ignoreWarnings = false, $leaveMessage = false ) {
+ $params = array(
+ 'action' => 'upload',
+ 'filename' => 'UploadFromUrlTest.png',
+ 'url' => 'http://bits.wikimedia.org/skins-1.5/common/images/poweredby_mediawiki_88x31.png',
+ 'asyncdownload' => 1,
+ 'token' => $token,
+ );
+ if ( $ignoreWarnings ) {
+ $params['ignorewarnings'] = 1;
+ }
+ if ( $leaveMessage ) {
+ $params['leavemessage'] = 1;
+ }
+
+ $data = $this->doApiRequest( $params );
+ $this->assertEquals( $data[0]['upload']['result'], 'Queued' );
+ $this->assertTrue( isset( $data[0]['upload']['statuskey'] ) );
+ $statusKey = $data[0]['upload']['statuskey'];
+
+ $job = Job::pop();
+ $this->assertEquals( 'UploadFromUrlJob', get_class( $job ) );
+
+ $status = $job->run();
+ $this->assertTrue( $status );
+
+ $data = $this->doApiRequest( array(
+ 'action' => 'upload',
+ 'statuskey' => $statusKey,
+ 'token' => $token,
+ ) );
+
+ return $data;
+ }
+
+
+ /**
+ *
+ */
+ protected function deleteFile( $name ) {
+ $t = Title::newFromText( $name, NS_FILE );
+ $this->assertTrue($t->exists(), "File '$name' exists");
+
+ if ( $t->exists() ) {
+ $file = wfFindFile( $name, array( 'ignoreRedirect' => true ) );
+ $empty = "";
+ FileDeleteForm::doDelete( $t, $file, $empty, "none", true );
+ $a = new Article ( $t );
+ $a->doDeleteArticle( "testing" );
+ }
+ $t = Title::newFromText( $name, NS_FILE );
+
+ $this->assertFalse($t->exists(), "File '$name' was deleted");
+ }
+ }
diff --git a/tests/phpunit/includes/upload/UploadStashTest.php b/tests/phpunit/includes/upload/UploadStashTest.php
new file mode 100644
index 00000000..9c39bc61
--- /dev/null
+++ b/tests/phpunit/includes/upload/UploadStashTest.php
@@ -0,0 +1,53 @@
+<?php
+
+class UploadStashTest extends MediaWikiTestCase {
+ /**
+ * @var Array of UploadStashTestUser
+ */
+ public static $users;
+
+ public function setUp() {
+ parent::setUp();
+
+ // Setup a file for bug 29408
+ $this->bug29408File = dirname( __FILE__ ) . '/bug29408';
+ file_put_contents( $this->bug29408File, "\x00" );
+
+ self::$users = array(
+ 'sysop' => new ApiTestUser(
+ 'Uploadstashtestsysop',
+ 'Upload Stash Test Sysop',
+ 'upload_stash_test_sysop@sample.com',
+ array( 'sysop' )
+ ),
+ 'uploader' => new ApiTestUser(
+ 'Uploadstashtestuser',
+ 'Upload Stash Test User',
+ 'upload_stash_test_user@sample.com',
+ array()
+ )
+ );
+ }
+
+ public function testBug29408() {
+ global $wgUser;
+ $wgUser = self::$users['uploader']->user;
+
+ $repo = RepoGroup::singleton()->getLocalRepo();
+ $stash = new UploadStash( $repo );
+
+ // Throws exception caught by PHPUnit on failure
+ $file = $stash->stashFile( $this->bug29408File );
+ // We'll never reach this point if we hit bug 29408
+ $this->assertTrue( true, 'Unrecognized file without extension' );
+
+ $stash->removeFile( $file->getFileKey() );
+ }
+
+ public function tearDown() {
+ parent::tearDown();
+
+ unlink( $this->bug29408File . "." );
+
+ }
+}
diff --git a/tests/phpunit/includes/upload/UploadTest.php b/tests/phpunit/includes/upload/UploadTest.php
new file mode 100644
index 00000000..69c29032
--- /dev/null
+++ b/tests/phpunit/includes/upload/UploadTest.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * @group Upload
+ */
+class UploadTest extends MediaWikiTestCase {
+ protected $upload;
+
+
+ function setUp() {
+ global $wgHooks;
+ parent::setUp();
+
+ $this->upload = new UploadTestHandler;
+ $this->hooks = $wgHooks;
+ $wgHooks['InterwikiLoadPrefix'][] = 'MediaWikiTestCase::disableInterwikis';
+ }
+
+ function tearDown() {
+ global $wgHooks;
+ $wgHooks = $this->hooks;
+ }
+
+ /**
+ * Test various forms of valid and invalid titles that can be supplied.
+ */
+ public function testTitleValidation() {
+
+
+ /* Test a valid title */
+ $this->assertUploadTitleAndCode( 'ValidTitle.jpg',
+ 'ValidTitle.jpg', UploadBase::OK,
+ 'upload valid title' );
+
+ /* A title with a slash */
+ $this->assertUploadTitleAndCode( 'A/B.jpg',
+ 'B.jpg', UploadBase::OK,
+ 'upload title with slash' );
+
+ /* A title with illegal char */
+ $this->assertUploadTitleAndCode( 'A:B.jpg',
+ 'A-B.jpg', UploadBase::OK,
+ 'upload title with colon' );
+
+ /* Stripping leading File: prefix */
+ $this->assertUploadTitleAndCode( 'File:C.jpg',
+ 'C.jpg', UploadBase::OK,
+ 'upload title with File prefix' );
+
+ /* Test illegal suggested title (r94601) */
+ $this->assertUploadTitleAndCode( '%281%29.JPG',
+ null, UploadBase::ILLEGAL_FILENAME,
+ 'illegal title for upload' );
+
+ /* A title without extension */
+ $this->assertUploadTitleAndCode( 'A',
+ null, UploadBase::FILETYPE_MISSING,
+ 'upload title without extension' );
+
+ /* A title with no basename */
+ $this->assertUploadTitleAndCode( '.jpg',
+ null, UploadBase::MIN_LENGTH_PARTNAME,
+ 'upload title without basename' );
+
+ }
+ /**
+ * Helper function for testTitleValidation. First checks the return code
+ * of UploadBase::getTitle() and then the actual returned titl
+ */
+ private function assertUploadTitleAndCode( $srcFilename, $dstFilename, $code, $msg ) {
+ /* Check the result code */
+ $this->assertEquals( $code,
+ $this->upload->testTitleValidation( $srcFilename ),
+ "$msg code" );
+
+ /* If we expect a valid title, check the title itself. */
+ if ( $code == UploadBase::OK ) {
+ $this->assertEquals( $dstFilename,
+ $this->upload->getTitle()->getText(),
+ "$msg text" );
+ }
+ }
+
+ /**
+ * Test the upload verification functions
+ */
+ public function testVerifyUpload() {
+ /* Setup with zero file size */
+ $this->upload->initializePathInfo( '', '', 0 );
+ $result = $this->upload->verifyUpload();
+ $this->assertEquals( UploadBase::EMPTY_FILE,
+ $result['status'],
+ 'upload empty file' );
+ }
+
+ // Helper used to create an empty file of size $size.
+ private function createFileOfSize( $size ) {
+ $filename = tempnam( wfTempDir(), "mwuploadtest" );
+
+ $fh = fopen( $filename, 'w' );
+ ftruncate( $fh, $size );
+ fclose( $fh );
+
+ return $filename;
+ }
+
+ /**
+ * test uploading a 100 bytes file with wgMaxUploadSize = 100
+ *
+ * This method should be abstracted so we can test different settings.
+ */
+
+ public function testMaxUploadSize() {
+ global $wgMaxUploadSize;
+ $savedGlobal = $wgMaxUploadSize; // save global
+ global $wgFileExtensions;
+ $wgFileExtensions[] = 'txt';
+
+ $wgMaxUploadSize = 100;
+
+ $filename = $this->createFileOfSize( $wgMaxUploadSize );
+ $this->upload->initializePathInfo( basename($filename) . '.txt', $filename, 100 );
+ $result = $this->upload->verifyUpload();
+ unlink( $filename );
+
+ $this->assertEquals(
+ array( 'status' => UploadBase::OK ), $result );
+
+ $wgMaxUploadSize = $savedGlobal; // restore global
+ }
+}
+
+class UploadTestHandler extends UploadBase {
+ public function initializeFromRequest( &$request ) { }
+ public function testTitleValidation( $name ) {
+ $this->mTitle = false;
+ $this->mDesiredDestName = $name;
+ $this->getTitle();
+ return $this->mTitleError;
+ }
+
+
+}
diff --git a/tests/phpunit/install-phpunit.sh b/tests/phpunit/install-phpunit.sh
new file mode 100644
index 00000000..2d2b53ab
--- /dev/null
+++ b/tests/phpunit/install-phpunit.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+has_binary () {
+ if [ -z `which $1` ]; then
+ return 1
+ fi
+ return 0
+}
+
+if [ `id -u` -ne 0 ]; then
+ echo '*** ERROR' Must be root to run
+ exit 1
+fi
+
+if ( has_binary phpunit ); then
+ echo PHPUnit already installed
+else if ( has_binary pear ); then
+ echo Installing phpunit with pear
+ pear channel-discover pear.phpunit.de
+ pear channel-discover components.ez.no
+ pear channel-discover pear.symfony-project.com
+ pear install phpunit/PHPUnit
+else if ( has_binary apt-get ); then
+ echo Installing phpunit with apt-get
+ apt-get install phpunit
+else if ( has_binary yum ); then
+ echo Installing phpunit with yum
+ yum install phpunit
+else if ( has_binary port ); then
+ echo Installing phpunit with macports
+ port install php5-unit
+fi
+fi
+fi
+fi
+fi
diff --git a/tests/phpunit/languages/LanguageBe_taraskTest.php b/tests/phpunit/languages/LanguageBe_taraskTest.php
new file mode 100644
index 00000000..e7fdb7ca
--- /dev/null
+++ b/tests/phpunit/languages/LanguageBe_taraskTest.php
@@ -0,0 +1,30 @@
+<?php
+
+class LanguageBeTaraskTest extends MediaWikiTestCase {
+ private $lang;
+
+ function setUp() {
+ $this->lang = Language::factory( 'Be-tarask' );
+ }
+ function tearDown() {
+ unset( $this->lang );
+ }
+
+ /** see bug 23156 & r64981 */
+ function testSearchRightSingleQuotationMarkAsApostroph() {
+ $this->assertEquals(
+ "'",
+ $this->lang->normalizeForSearch( '’' ),
+ 'bug 23156: U+2019 conversion to U+0027'
+ );
+ }
+ /** see bug 23156 & r64981 */
+ function testCommafy() {
+ $this->assertEquals( '1,234,567', $this->lang->commafy( '1234567' ) );
+ $this->assertEquals( '12,345', $this->lang->commafy( '12345' ) );
+ }
+ /** see bug 23156 & r64981 */
+ function testDoesNotCommafyFourDigitsNumber() {
+ $this->assertEquals( '1234', $this->lang->commafy( '1234' ) );
+ }
+}
diff --git a/tests/phpunit/languages/LanguageTest.php b/tests/phpunit/languages/LanguageTest.php
new file mode 100644
index 00000000..aaad9c31
--- /dev/null
+++ b/tests/phpunit/languages/LanguageTest.php
@@ -0,0 +1,246 @@
+<?php
+
+class LanguageTest extends MediaWikiTestCase {
+ private $lang;
+
+ function setUp() {
+ $this->lang = Language::factory( 'en' );
+ }
+ function tearDown() {
+ unset( $this->lang );
+ }
+
+ function testLanguageConvertDoubleWidthToSingleWidth() {
+ $this->assertEquals(
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
+ $this->lang->normalizeForSearch(
+ "ï¼ï¼‘23456789ABCDEFGHIJKLMNOPQRSTUVWXYZï½ï½‚cdefghijklï½ï½Žï½ï½ï½‘rstuvwxyz"
+ ),
+ 'convertDoubleWidth() with the full alphabet and digits'
+ );
+ }
+
+ function testFormatTimePeriod() {
+ $this->assertEquals(
+ "9.5s",
+ $this->lang->formatTimePeriod( 9.45 ),
+ 'formatTimePeriod() rounding (<10s)'
+ );
+
+ $this->assertEquals(
+ "10s",
+ $this->lang->formatTimePeriod( 9.95 ),
+ 'formatTimePeriod() rounding (<10s)'
+ );
+
+ $this->assertEquals(
+ "1m 0s",
+ $this->lang->formatTimePeriod( 59.55 ),
+ 'formatTimePeriod() rounding (<60s)'
+ );
+
+ $this->assertEquals(
+ "2m 0s",
+ $this->lang->formatTimePeriod( 119.55 ),
+ 'formatTimePeriod() rounding (<1h)'
+ );
+
+ $this->assertEquals(
+ "1h 0m 0s",
+ $this->lang->formatTimePeriod( 3599.55 ),
+ 'formatTimePeriod() rounding (<1h)'
+ );
+
+ $this->assertEquals(
+ "2h 0m 0s",
+ $this->lang->formatTimePeriod( 7199.55 ),
+ 'formatTimePeriod() rounding (>=1h)'
+ );
+
+ $this->assertEquals(
+ "2h 0m",
+ $this->lang->formatTimePeriod( 7199.55, 'avoidseconds' ),
+ 'formatTimePeriod() rounding (>=1h), avoidseconds'
+ );
+
+ $this->assertEquals(
+ "2h 0m",
+ $this->lang->formatTimePeriod( 7199.55, 'avoidminutes' ),
+ 'formatTimePeriod() rounding (>=1h), avoidminutes'
+ );
+
+ $this->assertEquals(
+ "48h 0m",
+ $this->lang->formatTimePeriod( 172799.55, 'avoidseconds' ),
+ 'formatTimePeriod() rounding (=48h), avoidseconds'
+ );
+
+ $this->assertEquals(
+ "3d 0h",
+ $this->lang->formatTimePeriod( 259199.55, 'avoidminutes' ),
+ 'formatTimePeriod() rounding (>48h), avoidminutes'
+ );
+
+ $this->assertEquals(
+ "2d 1h 0m",
+ $this->lang->formatTimePeriod( 176399.55, 'avoidseconds' ),
+ 'formatTimePeriod() rounding (>48h), avoidseconds'
+ );
+
+ $this->assertEquals(
+ "2d 1h",
+ $this->lang->formatTimePeriod( 176399.55, 'avoidminutes' ),
+ 'formatTimePeriod() rounding (>48h), avoidminutes'
+ );
+
+ $this->assertEquals(
+ "3d 0h 0m",
+ $this->lang->formatTimePeriod( 259199.55, 'avoidseconds' ),
+ 'formatTimePeriod() rounding (>48h), avoidminutes'
+ );
+
+ $this->assertEquals(
+ "2d 0h 0m",
+ $this->lang->formatTimePeriod( 172801.55, 'avoidseconds' ),
+ 'formatTimePeriod() rounding, (>48h), avoidseconds'
+ );
+
+ $this->assertEquals(
+ "2d 1h 1m 1s",
+ $this->lang->formatTimePeriod( 176460.55 ),
+ 'formatTimePeriod() rounding, recursion, (>48h)'
+ );
+ }
+
+ function testTruncate() {
+ $this->assertEquals(
+ "XXX",
+ $this->lang->truncate( "1234567890", 0, 'XXX' ),
+ 'truncate prefix, len 0, small ellipsis'
+ );
+
+ $this->assertEquals(
+ "12345XXX",
+ $this->lang->truncate( "1234567890", 8, 'XXX' ),
+ 'truncate prefix, small ellipsis'
+ );
+
+ $this->assertEquals(
+ "123456789",
+ $this->lang->truncate( "123456789", 5, 'XXXXXXXXXXXXXXX' ),
+ 'truncate prefix, large ellipsis'
+ );
+
+ $this->assertEquals(
+ "XXX67890",
+ $this->lang->truncate( "1234567890", -8, 'XXX' ),
+ 'truncate suffix, small ellipsis'
+ );
+
+ $this->assertEquals(
+ "123456789",
+ $this->lang->truncate( "123456789", -5, 'XXXXXXXXXXXXXXX' ),
+ 'truncate suffix, large ellipsis'
+ );
+ }
+
+ /**
+ * @dataProvider provideHTMLTruncateData()
+ */
+ function testTruncateHtml( $len, $ellipsis, $input, $expected ) {
+ // Actual HTML...
+ $this->assertEquals(
+ $expected,
+ $this->lang->truncateHTML( $input, $len, $ellipsis )
+ );
+ }
+
+ /**
+ * Array format is ($len, $ellipsis, $input, $expected)
+ */
+ function provideHTMLTruncateData() {
+ return array(
+ array( 0, 'XXX', "1234567890", "XXX" ),
+ array( 8, 'XXX', "1234567890", "12345XXX" ),
+ array( 5, 'XXXXXXXXXXXXXXX', '1234567890', "1234567890" ),
+ array( 2, '***',
+ '<p><span style="font-weight:bold;"></span></p>',
+ '<p><span style="font-weight:bold;"></span></p>',
+ ),
+ array( 2, '***',
+ '<p><span style="font-weight:bold;">123456789</span></p>',
+ '<p><span style="font-weight:bold;">***</span></p>',
+ ),
+ array( 2, '***',
+ '<p><span style="font-weight:bold;">&nbsp;23456789</span></p>',
+ '<p><span style="font-weight:bold;">***</span></p>',
+ ),
+ array( 3, '***',
+ '<p><span style="font-weight:bold;">123456789</span></p>',
+ '<p><span style="font-weight:bold;">***</span></p>',
+ ),
+ array( 4, '***',
+ '<p><span style="font-weight:bold;">123456789</span></p>',
+ '<p><span style="font-weight:bold;">1***</span></p>',
+ ),
+ array( 5, '***',
+ '<tt><span style="font-weight:bold;">123456789</span></tt>',
+ '<tt><span style="font-weight:bold;">12***</span></tt>',
+ ),
+ array( 6, '***',
+ '<p><a href="www.mediawiki.org">123456789</a></p>',
+ '<p><a href="www.mediawiki.org">123***</a></p>',
+ ),
+ array( 6, '***',
+ '<p><a href="www.mediawiki.org">12&nbsp;456789</a></p>',
+ '<p><a href="www.mediawiki.org">12&nbsp;***</a></p>',
+ ),
+ array( 7, '***',
+ '<small><span style="font-weight:bold;">123<p id="#moo">456</p>789</span></small>',
+ '<small><span style="font-weight:bold;">123<p id="#moo">4***</p></span></small>',
+ ),
+ array( 8, '***',
+ '<div><span style="font-weight:bold;">123<span>4</span>56789</span></div>',
+ '<div><span style="font-weight:bold;">123<span>4</span>5***</span></div>',
+ ),
+ array( 9, '***',
+ '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
+ '<p><table style="font-weight:bold;"><tr><td>123456789</td></tr></table></p>',
+ ),
+ array( 10, '***',
+ '<p><font style="font-weight:bold;">123456789</font></p>',
+ '<p><font style="font-weight:bold;">123456789</font></p>',
+ ),
+ );
+ }
+
+ /**
+ * Test Language::isValidBuiltInCode()
+ * @dataProvider provideLanguageCodes
+ */
+ function testBuiltInCodeValidation( $code, $message = '' ) {
+ $this->assertTrue(
+ (bool) Language::isValidBuiltInCode( $code ),
+ "validating code $code $message"
+ );
+ }
+
+ function testBuiltInCodeValidationRejectUnderscore() {
+ $this->assertFalse(
+ (bool) Language::isValidBuiltInCode( 'be_tarask' ),
+ "reject underscore in language code"
+ );
+ }
+
+ function provideLanguageCodes() {
+ return array(
+ array( 'fr' , 'Two letters, minor case' ),
+ array( 'EN' , 'Two letters, upper case' ),
+ array( 'tyv' , 'Three letters' ),
+ array( 'tokipona' , 'long language code' ),
+ array( 'be-tarask', 'With dash' ),
+ array( 'Zh-classical', 'Begin with upper case, dash' ),
+ array( 'Be-x-old', 'With extension (two dashes)' ),
+ );
+ }
+}
diff --git a/tests/phpunit/languages/LanguageTrTest.php b/tests/phpunit/languages/LanguageTrTest.php
new file mode 100644
index 00000000..d2a5ff36
--- /dev/null
+++ b/tests/phpunit/languages/LanguageTrTest.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * @author Ashar Voultoiz
+ * @copyright Copyright © 2011, Ashar Voultoiz
+ * @file
+ */
+
+/** Tests for MediaWiki languages/LanguageTr.php */
+class LanguageTrTest extends MediaWikiTestCase {
+ private $lang;
+
+ function setUp() {
+ $this->lang = Language::factory( 'Tr' );
+ }
+ function tearDown() {
+ unset( $this->lang );
+ }
+
+ /**
+ * See @bug 28040
+ * Credits to #wikipedia-tr users berm, []LuCkY[] and Emperyan
+ * @see http://en.wikipedia.org/wiki/Dotted_and_dotless_I
+ * @dataProvider provideDottedAndDotlessI
+ */
+ function testDottedAndDotlessI( $func, $input, $inputCase, $expected ) {
+ if( $func == 'ucfirst' ) {
+ $res = $this->lang->ucfirst( $input );
+ } elseif( $func == 'lcfirst' ) {
+ $res = $this->lang->lcfirst( $input );
+ } else {
+ throw new MWException( __METHOD__ . " given an invalid function name '$func'" );
+ }
+
+ $msg = "Converting $inputCase case '$input' with $func should give '$expected'";
+
+ $this->assertEquals( $expected, $res, $msg );
+ }
+
+ function provideDottedAndDotlessI() {
+ return array(
+ # function, input, input case, expected
+ # Case changed:
+ array( 'ucfirst', 'ı', 'lower', 'I' ),
+ array( 'ucfirst', 'i', 'lower', 'Ä°' ),
+ array( 'lcfirst', 'I', 'upper', 'ı' ),
+ array( 'lcfirst', 'Ä°', 'upper', 'i' ),
+
+ # Already using the correct case
+ array( 'ucfirst', 'I', 'upper', 'I' ),
+ array( 'ucfirst', 'Ä°', 'upper', 'Ä°' ),
+ array( 'lcfirst', 'ı', 'lower', 'ı' ),
+ array( 'lcfirst', 'i', 'lower', 'i' ),
+
+ # A real example taken from bug 28040 using
+ # http://tr.wikipedia.org/wiki/%C4%B0Phone
+ array( 'lcfirst', 'iPhone', 'lower', 'iPhone' ),
+
+ # next case is valid in Turkish but are different words if we
+ # consider IPhone is English!
+ array( 'lcfirst', 'IPhone', 'upper', 'ıPhone' ),
+
+ );
+ }
+
+}
diff --git a/tests/phpunit/phpunit.php b/tests/phpunit/phpunit.php
new file mode 100644
index 00000000..39cccf80
--- /dev/null
+++ b/tests/phpunit/phpunit.php
@@ -0,0 +1,61 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Bootstrapping for MediaWiki PHPUnit tests
+ *
+ * @file
+ */
+
+/* Configuration */
+
+// Evaluate the include path relative to this file
+$IP = dirname( dirname( dirname( __FILE__ ) ) );
+
+// Set a flag which can be used to detect when other scripts have been entered through this entry point or not
+define( 'MW_PHPUNIT_TEST', true );
+
+// Start up MediaWiki in command-line mode
+require_once( "$IP/maintenance/Maintenance.php" );
+
+class PHPUnitMaintClass extends Maintenance {
+ public function finalSetup() {
+ parent::finalSetup();
+
+ global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, $wgUseDatabaseMessages;
+ global $wgLocaltimezone, $wgLocalisationCacheConf;
+
+ $wgMainCacheType = CACHE_NONE;
+ $wgMessageCacheType = CACHE_NONE;
+ $wgParserCacheType = CACHE_NONE;
+
+ $wgUseDatabaseMessages = false; # Set for future resets
+
+ // Assume UTC for testing purposes
+ $wgLocaltimezone = 'UTC';
+
+ $wgLocalisationCacheConf['storeClass'] = 'LCStore_Null';
+ }
+ public function execute() { }
+ public function getDbType() {
+ return Maintenance::DB_ADMIN;
+ }
+}
+
+$maintClass = 'PHPUnitMaintClass';
+require( RUN_MAINTENANCE_IF_MAIN );
+
+if( !in_array( '--configuration', $_SERVER['argv'] ) ) {
+ //Hack to eliminate the need to use the Makefile (which sucks ATM)
+ $_SERVER['argv'][] = '--configuration';
+ $_SERVER['argv'][] = $IP . '/tests/phpunit/suite.xml';
+}
+
+require_once( 'PHPUnit/Runner/Version.php' );
+if( version_compare( PHPUnit_Runner_Version::id(), '3.5.0', '<' ) ) {
+ die( 'PHPUnit 3.5 or later required, you have ' . PHPUnit_Runner_Version::id() . ".\n" );
+}
+require_once( 'PHPUnit/Autoload.php' );
+
+require_once( "$IP/tests/TestsAutoLoader.php" );
+MediaWikiPHPUnitCommand::main();
+
diff --git a/tests/phpunit/run-tests.bat b/tests/phpunit/run-tests.bat
new file mode 100644
index 00000000..e6eb3e0c
--- /dev/null
+++ b/tests/phpunit/run-tests.bat
@@ -0,0 +1 @@
+php phpunit.php --configuration suite.xml %*
diff --git a/tests/phpunit/skins/SideBarTest.php b/tests/phpunit/skins/SideBarTest.php
new file mode 100644
index 00000000..47182a71
--- /dev/null
+++ b/tests/phpunit/skins/SideBarTest.php
@@ -0,0 +1,175 @@
+<?php
+
+/**
+ * @group Skin
+ */
+class SideBarTest extends MediaWikiLangTestCase {
+
+ /** A skin template, reinitialized before each test */
+ private $skin;
+ /** Local cache for sidebar messages */
+ private $messages;
+
+ function __construct() {
+ parent::__construct();
+ }
+
+ /** Build $this->messages array */
+ private function initMessagesHref() {
+ # List of default messages for the sidebar:
+ $URL_messages = array(
+ 'mainpage',
+ 'portal-url',
+ 'currentevents-url',
+ 'recentchanges-url',
+ 'randompage-url',
+ 'helppage',
+ );
+
+ foreach( $URL_messages as $m ) {
+ $titleName = MessageCache::singleton()->get($m);
+ $title = Title::newFromText( $titleName );
+ $this->messages[$m]['href'] = $title->getLocalURL();
+ }
+ }
+
+ function setUp() {
+ parent::setUp();
+ $this->initMessagesHref();
+ $this->skin = new SkinTemplate();
+ }
+ function tearDown() {
+ parent::tearDown();
+ $this->skin = null;
+ }
+
+ /**
+ * Internal helper to test the sidebar
+ * @param $expected
+ * @param $text
+ * @param $message (Default: '')
+ */
+ private function assertSideBar( $expected, $text, $message = '' ) {
+ $bar = array();
+ $this->skin->addToSidebarPlain( $bar, $text );
+ $this->assertEquals( $expected, $bar, $message );
+ }
+
+ function testSidebarWithOnlyTwoTitles() {
+ $this->assertSideBar(
+ array(
+ 'Title1' => array(),
+ 'Title2' => array(),
+ ),
+'* Title1
+* Title2
+'
+ );
+ }
+
+ function testExpandMessages() {
+ $this->assertSidebar(
+ array( 'Title' => array(
+ array(
+ 'text' => 'Help',
+ 'href' => $this->messages['helppage']['href'],
+ 'id' => 'n-help',
+ 'active' => null
+ )
+ )),
+'* Title
+** helppage|help
+'
+ );
+ }
+
+ function testExternalUrlsRequireADescription() {
+ $this->assertSidebar(
+ array( 'Title' => array(
+ # ** http://www.mediawiki.org/| Home
+ array(
+ 'text' => 'Home',
+ 'href' => 'http://www.mediawiki.org/',
+ 'id' => 'n-Home',
+ 'active' => null,
+ 'rel' => 'nofollow',
+ ),
+ # ** http://valid.no.desc.org/
+ # ... skipped since it is missing a pipe with a description
+ )),
+'* Title
+** http://www.mediawiki.org/| Home
+** http://valid.no.desc.org/
+'
+
+ );
+
+ }
+
+
+ #### Attributes for external links ##########################
+ private function getAttribs( ) {
+ # Sidebar text we will use everytime
+ $text = '* Title
+** http://www.mediawiki.org/| Home';
+
+ $bar = array();
+ $this->skin->addToSideBarPlain( $bar, $text );
+
+ return $bar['Title'][0];
+ }
+
+ /**
+ * Simple test to verify our helper assertAttribs() is functional
+ * Please note this assume MediaWiki default settings:
+ * $wgNoFollowLinks = true
+ * $wgExternalLinkTarget = false
+ */
+ function testTestAttributesAssertionHelper() {
+ $attribs = $this->getAttribs();
+
+ $this->assertArrayHasKey( 'rel', $attribs );
+ $this->assertEquals( 'nofollow', $attribs['rel'] );
+
+ $this->assertArrayNotHasKey( 'target', $attribs );
+ }
+
+ /**
+ * Test wgNoFollowLinks in sidebar
+ */
+ function testRespectWgnofollowlinks() {
+ global $wgNoFollowLinks;
+ $saved = $wgNoFollowLinks;
+ $wgNoFollowLinks = false;
+
+ $attribs = $this->getAttribs();
+ $this->assertArrayNotHasKey( 'rel', $attribs,
+ 'External URL in sidebar do not have rel=nofollow when wgNoFollowLinks = false'
+ );
+
+ // Restore global
+ $wgNoFollowLinks = $saved;
+ }
+
+ /**
+ * Test wgExternaLinkTarget in sidebar
+ */
+ function testRespectExternallinktarget() {
+ global $wgExternalLinkTarget;
+ $saved = $wgExternalLinkTarget;
+
+ $wgExternalLinkTarget = '_blank';
+ $attribs = $this->getAttribs();
+ $this->assertArrayHasKey( 'target', $attribs );
+ $this->assertEquals( $attribs['target'], '_blank' );
+
+ $wgExternalLinkTarget = '_self';
+ $attribs = $this->getAttribs();
+ $this->assertArrayHasKey( 'target', $attribs );
+ $this->assertEquals( $attribs['target'], '_self' );
+
+ // Restore global
+ $wgExternalLinkTarget = $saved;
+ }
+
+}
diff --git a/tests/phpunit/suite.xml b/tests/phpunit/suite.xml
new file mode 100644
index 00000000..e6649beb
--- /dev/null
+++ b/tests/phpunit/suite.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- colors don't work on Windows! -->
+<phpunit bootstrap="./bootstrap.php"
+ colors="false"
+ backupGlobals="false"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ stopOnFailure="false"
+ strict="true"
+ verbose="true">
+ <testsuites>
+ <testsuite name="includes">
+ <directory>./includes</directory>
+ </testsuite>
+ <testsuite name="languages">
+ <directory>./languages</directory>
+ </testsuite>
+ <testsuite name="skins">
+ <directory>./skins</directory>
+ </testsuite>
+ <testsuite name="uploadfromurl">
+ <file>./suites/UploadFromUrlTestSuite.php</file>
+ </testsuite>
+ <testsuite name="extensions">
+ <file>./suites/ExtensionsTestSuite.php</file>
+ </testsuite>
+ </testsuites>
+ <groups>
+ <exclude>
+ <group>Utility</group>
+ <group>Broken</group>
+ <group>Stub</group>
+ </exclude>
+ </groups>
+</phpunit>
diff --git a/tests/phpunit/suites/ExtensionsTestSuite.php b/tests/phpunit/suites/ExtensionsTestSuite.php
new file mode 100644
index 00000000..d728807f
--- /dev/null
+++ b/tests/phpunit/suites/ExtensionsTestSuite.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * This test suite runs unit tests registered by extensions.
+ * See http://www.mediawiki.org/wiki/Manual:Hooks/UnitTestsList for details of how to register your tests.
+ */
+
+class ExtensionsTestSuite extends PHPUnit_Framework_TestSuite {
+ public function __construct() {
+ parent::__construct();
+ $files = array();
+ wfRunHooks( 'UnitTestsList', array( &$files ) );
+ foreach ( $files as $file ) {
+ $this->addTestFile( $file );
+ }
+ if ( !count( $files ) ) {
+ $this->addTest( new DummyExtensionsTest( 'testNothing' ) );
+ }
+ }
+
+ public static function suite() {
+ return new self;
+ }
+}
+
+/**
+ * Needed to avoid warnings like 'No tests found in class "ExtensionsTestSuite".'
+ * when no extensions with tests are used.
+ */
+class DummyExtensionsTest extends MediaWikiTestCase {
+ public function testNothing() {
+ $this->assertTrue( true );
+ }
+}
diff --git a/tests/phpunit/suites/UploadFromUrlTestSuite.php b/tests/phpunit/suites/UploadFromUrlTestSuite.php
new file mode 100644
index 00000000..9b666825
--- /dev/null
+++ b/tests/phpunit/suites/UploadFromUrlTestSuite.php
@@ -0,0 +1,178 @@
+<?php
+
+require_once( dirname( dirname( __FILE__ ) ) . '/includes/upload/UploadFromUrlTest.php' );
+
+class UploadFromUrlTestSuite extends PHPUnit_Framework_TestSuite {
+ public static function addTables( &$tables ) {
+ $tables[] = 'user_properties';
+ $tables[] = 'filearchive';
+ $tables[] = 'logging';
+ $tables[] = 'updatelog';
+ $tables[] = 'iwlinks';
+
+ return true;
+ }
+
+ function setUp() {
+ global $wgParser, $wgParserConf, $IP, $messageMemc, $wgMemc, $wgDeferredUpdateList,
+ $wgUser, $wgLang, $wgOut, $wgRequest, $wgStyleDirectory, $wgEnableParserCache,
+ $wgNamespaceAliases, $wgNamespaceProtection, $wgLocalFileRepo,
+ $parserMemc, $wgThumbnailScriptPath, $wgScriptPath,
+ $wgArticlePath, $wgStyleSheetPath, $wgScript, $wgStylePath;
+
+ $wgScript = '/index.php';
+ $wgScriptPath = '/';
+ $wgArticlePath = '/wiki/$1';
+ $wgStyleSheetPath = '/skins';
+ $wgStylePath = '/skins';
+ $wgThumbnailScriptPath = false;
+ $wgLocalFileRepo = array(
+ 'class' => 'LocalRepo',
+ 'name' => 'local',
+ 'directory' => wfTempDir() . '/test-repo',
+ 'url' => 'http://example.com/images',
+ 'deletedDir' => wfTempDir() . '/test-repo/delete',
+ 'hashLevels' => 2,
+ 'transformVia404' => false,
+ );
+ $wgNamespaceProtection[NS_MEDIAWIKI] = 'editinterface';
+ $wgNamespaceAliases['Image'] = NS_FILE;
+ $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK;
+
+
+ $wgEnableParserCache = false;
+ $wgDeferredUpdateList = array();
+ $wgMemc = wfGetMainCache();
+ $messageMemc = wfGetMessageCacheStorage();
+ $parserMemc = wfGetParserCacheStorage();
+
+ // $wgContLang = new StubContLang;
+ $wgUser = new User;
+ $context = new RequestContext;
+ $wgLang = $context->getLang();
+ $wgOut = $context->getOutput();
+ $wgParser = new StubObject( 'wgParser', $wgParserConf['class'], array( $wgParserConf ) );
+ $wgRequest = new WebRequest;
+
+ if ( $wgStyleDirectory === false ) {
+ $wgStyleDirectory = "$IP/skins";
+ }
+
+ }
+
+ public function tearDown() {
+ $this->teardownUploadDir( $this->uploadDir );
+ }
+
+ private $uploadDir;
+ private $keepUploads;
+
+ /**
+ * Remove the dummy uploads directory
+ */
+ private function teardownUploadDir( $dir ) {
+ if ( $this->keepUploads ) {
+ return;
+ }
+
+ // delete the files first, then the dirs.
+ self::deleteFiles(
+ array (
+ "$dir/3/3a/Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/200px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/640px-Foobar.jpg",
+ "$dir/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg",
+
+ "$dir/0/09/Bad.jpg",
+ )
+ );
+
+ self::deleteDirs(
+ array (
+ "$dir/3/3a",
+ "$dir/3",
+ "$dir/thumb/6/65",
+ "$dir/thumb/6",
+ "$dir/thumb/3/3a/Foobar.jpg",
+ "$dir/thumb/3/3a",
+ "$dir/thumb/3",
+
+ "$dir/0/09/",
+ "$dir/0/",
+
+ "$dir/thumb",
+ "$dir",
+ )
+ );
+ }
+
+ /**
+ * Delete the specified files, if they exist.
+ *
+ * @param $files Array: full paths to files to delete.
+ */
+ private static function deleteFiles( $files ) {
+ foreach ( $files as $file ) {
+ if ( file_exists( $file ) ) {
+ unlink( $file );
+ }
+ }
+ }
+
+ /**
+ * Delete the specified directories, if they exist. Must be empty.
+ *
+ * @param $dirs Array: full paths to directories to delete.
+ */
+ private static function deleteDirs( $dirs ) {
+ foreach ( $dirs as $dir ) {
+ if ( is_dir( $dir ) ) {
+ rmdir( $dir );
+ }
+ }
+ }
+
+ /**
+ * Create a dummy uploads directory which will contain a couple
+ * of files in order to pass existence tests.
+ *
+ * @return String: the directory
+ */
+ private function setupUploadDir() {
+ global $IP;
+
+ if ( $this->keepUploads ) {
+ $dir = wfTempDir() . '/mwParser-images';
+
+ if ( is_dir( $dir ) ) {
+ return $dir;
+ }
+ } else {
+ $dir = wfTempDir() . "/mwParser-" . mt_rand() . "-images";
+ }
+
+ wfDebug( "Creating upload directory $dir\n" );
+
+ if ( file_exists( $dir ) ) {
+ wfDebug( "Already exists!\n" );
+ return $dir;
+ }
+
+ wfMkdirParents( $dir . '/3/3a' );
+ copy( "$IP/skins/monobook/headbg.jpg", "$dir/3/3a/Foobar.jpg" );
+
+ wfMkdirParents( $dir . '/0/09' );
+ copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" );
+
+ return $dir;
+ }
+
+ public static function suite() {
+ // Hack to invoke the autoloader required to get phpunit to recognize
+ // the UploadFromUrlTest class
+ class_exists( 'UploadFromUrlTest' );
+ $suite = new UploadFromUrlTestSuite( 'UploadFromUrlTest' );
+ return $suite;
+ }
+}
diff --git a/tests/qunit/.htaccess b/tests/qunit/.htaccess
new file mode 100644
index 00000000..605d2f4c
--- /dev/null
+++ b/tests/qunit/.htaccess
@@ -0,0 +1 @@
+Allow from all
diff --git a/tests/qunit/data/defineTestCallback.js b/tests/qunit/data/defineTestCallback.js
new file mode 100644
index 00000000..6fcd4595
--- /dev/null
+++ b/tests/qunit/data/defineTestCallback.js
@@ -0,0 +1,4 @@
+window.mw.loader.testCallback = function() {
+ start();
+ ok( true, 'Implementing a module, is the callback timed properly ?');
+};
diff --git a/tests/qunit/data/testrunner.js b/tests/qunit/data/testrunner.js
new file mode 100644
index 00000000..dbfe9fad
--- /dev/null
+++ b/tests/qunit/data/testrunner.js
@@ -0,0 +1,92 @@
+( function( $ ) {
+
+/**
+ * Add bogus to url to prevent IE crazy caching
+ *
+ * @param value {String} a relative path (eg. 'data/defineTestCallback.js' or 'data/test.php?foo=bar')
+ * @return {String} Such as 'data/defineTestCallback.js?131031765087663960'
+ */
+QUnit.fixurl = function(value) {
+ return value + (/\?/.test(value) ? "&" : "?") + new Date().getTime() + "" + parseInt(Math.random()*100000);
+};
+
+/**
+ * Load TestSwarm agent
+ */
+if ( QUnit.urlParams.swarmURL ) {
+ document.write("<scr" + "ipt src='" + QUnit.fixurl( 'data/testwarm.inject.js' ) + "'></scr" + "ipt>");
+}
+
+/**
+ * Load completenesstest
+ */
+if ( QUnit.urlParams.completenesstest ) {
+
+ // Return true to ignore
+ var mwTestIgnore = function( val, tester, funcPath ) {
+
+ // Don't record methods of the properties of constructors,
+ // to avoid getting into a loop (prototype.constructor.prototype..).
+ // Since we're therefor skipping any injection for
+ // "new mw.Foo()", manually set it to true here.
+ if ( val instanceof mw.Map ) {
+ tester.methodCallTracker['Map'] = true;
+ return true;
+ }
+
+ // Don't record methods of the properties of a jQuery object
+ if ( val instanceof $ ) {
+ return true;
+ }
+
+ return false;
+ };
+
+ var mwTester = new CompletenessTest( mw, mwTestIgnore );
+}
+
+/**
+ * Add-on assertion helpers
+ */
+// Define the add-ons
+var addons = {
+
+ // Expect boolean true
+ assertTrue: function( actual, message ) {
+ strictEqual( actual, true, message );
+ },
+
+ // Expect boolean false
+ assertFalse: function( actual, message ) {
+ strictEqual( actual, false, message );
+ },
+
+ // Expect numerical value less than X
+ lt: function( actual, expected, message ) {
+ QUnit.push( actual < expected, actual, 'less than ' + expected, message );
+ },
+
+ // Expect numerical value less than or equal to X
+ ltOrEq: function( actual, expected, message ) {
+ QUnit.push( actual <= expected, actual, 'less than or equal to ' + expected, message );
+ },
+
+ // Expect numerical value greater than X
+ gt: function( actual, expected, message ) {
+ QUnit.push( actual > expected, actual, 'greater than ' + expected, message );
+ },
+
+ // Expect numerical value greater than or equal to X
+ gtOrEq: function( actual, expected, message ) {
+ QUnit.push( actual >= expected, actual, 'greater than or equal to ' + expected, message );
+ },
+
+ // Backwards compatible with new verions of QUnit
+ equals: window.equal,
+ same: window.deepEqual
+};
+
+$.extend( QUnit, addons );
+$.extend( window, addons );
+
+})( jQuery );
diff --git a/tests/qunit/data/testwarm.inject.js b/tests/qunit/data/testwarm.inject.js
new file mode 100644
index 00000000..14ee8f93
--- /dev/null
+++ b/tests/qunit/data/testwarm.inject.js
@@ -0,0 +1,349 @@
+/*
+ Copyright (c) 2009 John Resig
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+(function(){
+
+ var DEBUG = false;
+
+ var doPost = false;
+
+ try {
+ doPost = !!window.top.postMessage;
+ } catch(e){}
+
+ var search = window.location.search,
+ url, index;
+ if( ( index = search.indexOf( "swarmURL=" ) ) != -1 )
+ url = decodeURIComponent( search.slice( index + 9 ) );
+
+ if ( !DEBUG && (!url || url.indexOf("http") !== 0) ) {
+ return;
+ }
+
+ var submitTimeout = 5;
+
+ var curHeartbeat;
+ var beatRate = 20;
+
+ // Expose the TestSwarm API
+ window.TestSwarm = {
+ submit: submit,
+ heartbeat: function(){
+ if ( curHeartbeat ) {
+ clearTimeout( curHeartbeat );
+ }
+
+ curHeartbeat = setTimeout(function(){
+ submit({ fail: -1, total: -1 });
+ }, beatRate * 1000);
+ },
+ serialize: function(){
+ return trimSerialize();
+ }
+ };
+
+ // Prevent careless things from executing
+ window.print = window.confirm = window.alert = window.open = function(){};
+
+ window.onerror = function(e){
+ document.body.appendChild( document.createTextNode( "ERROR: " + e ));
+ submit({ fail: 0, error: 1, total: 1 });
+ return false;
+ };
+
+ // QUnit (jQuery)
+ // http://docs.jquery.com/QUnit
+ if ( typeof QUnit !== "undefined" ) {
+ QUnit.done = function(results){
+ submit({
+ fail: results.failed,
+ error: 0,
+ total: results.total
+ });
+ };
+
+ QUnit.log = window.TestSwarm.heartbeat;
+ window.TestSwarm.heartbeat();
+
+ window.TestSwarm.serialize = function(){
+ // Clean up the HTML (remove any un-needed test markup)
+ remove("nothiddendiv");
+ remove("loadediframe");
+ remove("dl");
+ remove("main");
+
+ // Show any collapsed results
+ var ol = document.getElementsByTagName("ol");
+ for ( var i = 0; i < ol.length; i++ ) {
+ ol[i].style.display = "block";
+ }
+
+ return trimSerialize();
+ };
+
+ // UnitTestJS (Prototype, Scriptaculous)
+ // http://github.com/tobie/unittest_js/tree/master
+ } else if ( typeof Test !== "undefined" && Test && Test.Unit && Test.Unit.runners ) {
+ var total_runners = Test.Unit.runners.length, cur_runners = 0;
+ var total = 0, fail = 0, error = 0;
+
+ for (var i = 0; i < Test.Unit.runners.length; i++) (function(i){
+ var finish = Test.Unit.runners[i].finish;
+ Test.Unit.runners[i].finish = function(){
+ finish.call( this );
+
+ var results = this.getResult();
+ total += results.assertions;
+ fail += results.failures;
+ error += results.errors;
+
+ if ( ++cur_runners === total_runners ) {
+ submit({
+ fail: fail,
+ error: error,
+ total: total
+ });
+ }
+ };
+ })(i);
+
+ // JSSpec (MooTools)
+ // http://jania.pe.kr/aw/moin.cgi/JSSpec
+ } else if ( typeof JSSpec !== "undefined" && JSSpec && JSSpec.Logger ) {
+ var onRunnerEnd = JSSpec.Logger.prototype.onRunnerEnd;
+ JSSpec.Logger.prototype.onRunnerEnd = function(){
+ onRunnerEnd.call(this);
+
+ // Show any collapsed results
+ var ul = document.getElementsByTagName("ul");
+ for ( var i = 0; i < ul.length; i++ ) {
+ ul[i].style.display = "block";
+ }
+
+ submit({
+ fail: JSSpec.runner.getTotalFailures(),
+ error: JSSpec.runner.getTotalErrors(),
+ total: JSSpec.runner.totalExamples
+ });
+ };
+
+ window.TestSwarm.serialize = function(){
+ // Show any collapsed results
+ var ul = document.getElementsByTagName("ul");
+ for ( var i = 0; i < ul.length; i++ ) {
+ ul[i].style.display = "block";
+ }
+
+ return trimSerialize();
+ };
+
+ // JSUnit
+ // http://www.jsunit.net/
+ // Note: Injection file must be included before the frames
+ // are document.write()d into the page.
+ } else if ( typeof JsUnitTestManager !== "undefined" ) {
+ var _done = JsUnitTestManager.prototype._done;
+ JsUnitTestManager.prototype._done = function(){
+ _done.call(this);
+
+ submit({
+ fail: this.failureCount,
+ error: this.errorCount,
+ total: this.totalCount
+ });
+ };
+
+ window.TestSwarm.serialize = function(){
+ return "<pre>" + this.log.join("\n") + "</pre>";
+ };
+
+ // Selenium Core
+ // http://seleniumhq.org/projects/core/
+ } else if ( typeof SeleniumTestResult !== "undefined" && typeof LOG !== "undefined" ) {
+ // Completely overwrite the postback
+ SeleniumTestResult.prototype.post = function(){
+ submit({
+ fail: this.metrics.numCommandFailures,
+ error: this.metrics.numCommandErrors,
+ total: this.metrics.numCommandPasses + this.metrics.numCommandFailures + this.metrics.numCommandErrors
+ });
+ };
+
+ window.TestSwarm.serialize = function(){
+ var results = [];
+ while ( LOG.pendingMessages.length ) {
+ var msg = LOG.pendingMessages.shift();
+ results.push( msg.type + ": " + msg.msg );
+ }
+
+ return "<pre>" + results.join("\n") + "</pre>";
+ };
+
+ // Dojo Objective Harness
+ // http://docs.dojocampus.org/quickstart/doh
+ } else if ( typeof doh !== "undefined" && doh._report ) {
+ var _report = doh._report;
+ doh._report = function(){
+ _report.apply(this, arguments);
+
+ submit({
+ fail: doh._failureCount,
+ error: doh._errorCount,
+ total: doh._testCount
+ });
+ };
+
+ window.TestSwarm.serialize = function(){
+ return "<pre>" + document.getElementById("logBody").innerHTML + "</pre>";
+ };
+ // Screw.Unit
+ // git://github.com/nathansobo/screw-unit.git
+ } else if ( typeof Screw !== "undefined" && typeof jQuery !== 'undefined' && Screw && Screw.Unit ) {
+ $(Screw).bind("after", function() {
+ var passed = $('.passed').length;
+ var failed = $('.failed').length;
+ submit({
+ fail: failed,
+ error: 0,
+ total: failed + passed
+ });
+ });
+
+ $(Screw).bind("loaded", function() {
+ $('.it')
+ .bind("passed", window.TestSwarm.heartbeat)
+ .bind("failed", window.TestSwarm.heartbeat);
+ window.TestSwarm.heartbeat();
+ });
+
+ window.TestSwarm.serialize = function(){
+ return trimSerialize();
+ };
+ }
+
+ function trimSerialize(doc) {
+ doc = doc || document;
+
+ var scripts = doc.getElementsByTagName("script");
+ while ( scripts.length ) {
+ remove( scripts[0] );
+ }
+
+ var root = window.location.href.replace(/(https?:\/\/.*?)\/.*/, "$1");
+ var cur = window.location.href.replace(/[^\/]*$/, "");
+
+ var links = doc.getElementsByTagName("link");
+ for ( var i = 0; i < links.length; i++ ) {
+ var href = links[i].href;
+ if ( href.indexOf("/") === 0 ) {
+ href = root + href;
+ } else if ( !/^https?:\/\//.test( href ) ) {
+ href = cur + href;
+ }
+ links[i].href = href;
+ }
+
+ return ("<html>" + doc.documentElement.innerHTML + "</html>")
+ .replace(/\s+/g, " ");
+ }
+
+ function remove(elem){
+ if ( typeof elem === "string" ) {
+ elem = document.getElementById( elem );
+ }
+
+ if ( elem ) {
+ elem.parentNode.removeChild( elem );
+ }
+ }
+
+ function submit(params){
+ if ( curHeartbeat ) {
+ clearTimeout( curHeartbeat );
+ }
+
+ var paramItems = (url.split("?")[1] || "").split("&");
+
+ for ( var i = 0; i < paramItems.length; i++ ) {
+ if ( paramItems[i] ) {
+ var parts = paramItems[i].split("=");
+ if ( !params[ parts[0] ] ) {
+ params[ parts[0] ] = parts[1];
+ }
+ }
+ }
+
+ if ( !params.state ) {
+ params.state = "saverun";
+ }
+
+ if ( !params.results ) {
+ params.results = window.TestSwarm.serialize();
+ }
+
+ if ( doPost ) {
+ // Build Query String
+ var query = "";
+
+ for ( var i in params ) {
+ query += ( query ? "&" : "" ) + i + "=" +
+ encodeURIComponent(params[i]);
+ }
+
+ if ( DEBUG ) {
+ alert( query );
+ } else {
+ window.top.postMessage( query, "*" );
+ }
+
+ } else {
+ var form = document.createElement("form");
+ form.action = url;
+ form.method = "POST";
+
+ for ( var i in params ) {
+ var input = document.createElement("input");
+ input.type = "hidden";
+ input.name = i;
+ input.value = params[i];
+ form.appendChild( input );
+ }
+
+ if ( DEBUG ) {
+ alert( form.innerHTML );
+ } else {
+
+ // Watch for the result submission timing out
+ setTimeout(function(){
+ submit( params );
+ }, submitTimeout * 1000);
+
+ document.body.appendChild( form );
+ form.submit();
+ }
+ }
+ }
+
+})();
diff --git a/tests/qunit/index.html b/tests/qunit/index.html
new file mode 100644
index 00000000..d4bbe7e2
--- /dev/null
+++ b/tests/qunit/index.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>MediaWiki JavaScript Test Suite</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <!-- MediaWiki Modules -->
+
+ <!-- MW: startup -->
+ <script>
+ function startUp(){
+ mw.config = new mw.Map( false );
+ }
+ </script>
+
+ <!-- MW: jquery|mediawiki -->
+ <script src="../../resources/jquery/jquery.js"></script>
+ <script src="../../resources/mediawiki/mediawiki.js"></script>
+
+ <!-- MW: mediawiki.page.startup -->
+ <script src="../../resources/jquery/jquery.client.js"></script>
+ <script src="../../resources/mediawiki.page/mediawiki.page.startup.js"></script>
+
+ <!-- MW: mediawiki.user|mediawiki.util|mediawiki.page.ready -->
+ <script src="../../resources/jquery/jquery.cookie.js"></script>
+ <script src="../../resources/mediawiki/mediawiki.user.js"></script>
+
+ <script src="../../resources/jquery/jquery.messageBox.js"></script>
+ <script src="../../resources/jquery/jquery.mwPrototypes.js"></script>
+ <script src="../../resources/mediawiki/mediawiki.util.js"></script>
+
+ <script src="../../resources/jquery/jquery.checkboxShiftClick.js"></script>
+ <script src="../../resources/jquery/jquery.makeCollapsible.js"></script>
+ <script src="../../resources/jquery/jquery.placeholder.js"></script>
+ <script src="../../resources/mediawiki.page/mediawiki.page.ready.js"></script>
+
+ <!-- MW: user.options -->
+ <script>
+ mw.user.options.set({"skin": "vector"});
+ </script>
+
+ <!-- MW: Non-default modules -->
+ <script src="../../resources/jquery/jquery.autoEllipsis.js"></script>
+ <script src="../../resources/jquery/jquery.byteLength.js"></script>
+ <script src="../../resources/jquery/jquery.byteLimit.js"></script>
+ <script src="../../resources/jquery/jquery.colorUtil.js"></script>
+ <script src="../../resources/jquery/jquery.getAttrs.js"></script>
+ <script src="../../resources/jquery/jquery.localize.js"></script>
+ <script src="../../resources/jquery/jquery.tabIndex.js"></script>
+ <script src="../../resources/jquery/jquery.tablesorter.js"></script>
+ <script src="../../resources/mediawiki.special/mediawiki.special.js"></script>
+ <script src="../../resources/mediawiki.special/mediawiki.special.recentchanges.js"></script>
+
+ <!-- QUnit: Load framework -->
+ <link rel="stylesheet" href="../../resources/jquery/jquery.qunit.css" />
+ <script src="../../resources/jquery/jquery.qunit.js"></script>
+ <script src="../../resources/jquery/jquery.qunit.completenessTest.js"></script>
+ <script src="data/testrunner.js"></script>
+
+ <!-- QUnit: Load test suites (maintain the same order as above please) -->
+ <script src="suites/resources/mediawiki/mediawiki.jscompat.test.js"></script>
+ <script src="suites/resources/mediawiki/mediawiki.js"></script>
+ <script src="suites/resources/mediawiki/mediawiki.user.js"></script>
+
+ <script src="suites/resources/jquery/jquery.client.js"></script>
+ <script src="suites/resources/jquery/jquery.mwPrototypes.js"></script>
+ <script src="suites/resources/mediawiki/mediawiki.util.js"></script>
+
+ <script src="suites/resources/jquery/jquery.autoEllipsis.js"></script>
+ <script src="suites/resources/jquery/jquery.byteLength.js"></script>
+ <script src="suites/resources/jquery/jquery.byteLimit.js"></script>
+ <script src="suites/resources/jquery/jquery.colorUtil.js"></script>
+ <script src="suites/resources/jquery/jquery.getAttrs.js"></script>
+ <script src="suites/resources/jquery/jquery.localize.js"></script>
+ <script src="suites/resources/jquery/jquery.tabIndex.js"></script>
+ <script src="suites/resources/jquery/jquery.tablesorter.test.js" charset="UTF-8"></script>
+ <script src="suites/resources/mediawiki.special/mediawiki.special.recentchanges.js"></script>
+</head>
+<body>
+ <h1 id="qunit-header">MediaWiki JavaScript Test Suite</h1>
+ <h2 id="qunit-banner"></h2>
+ <div id="qunit-testrunner-toolbar">
+ <p><a href="http://www.mediawiki.org/wiki/Manual:JavaScript_unit_testing">See testing documentation on mediawiki.org</a></p>
+ </div>
+ <h2 id="qunit-userAgent"></h2>
+ <ol id="qunit-tests"></ol>
+
+<!-- Scripts inserting stuff here shall remove it themselfs! -->
+<div id="content"></div>
+</body>
+</html>
diff --git a/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js b/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js
new file mode 100644
index 00000000..caf5a6f1
--- /dev/null
+++ b/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js
@@ -0,0 +1,58 @@
+module( 'jquery.autoEllipsis.js' );
+
+test( '-- Initial check', function() {
+ expect(1);
+ ok( $.fn.autoEllipsis, 'jQuery.fn.autoEllipsis defined' );
+});
+
+function createWrappedDiv( text, width ) {
+ var $wrapper = $( '<div />' ).css( 'width', width );
+ var $div = $( '<div />' ).text( text );
+ $wrapper.append( $div );
+ return $wrapper;
+}
+
+function findDivergenceIndex( a, b ) {
+ var i = 0;
+ while ( i < a.length && i < b.length && a[i] == b[i] ) {
+ i++;
+ }
+ return i;
+}
+
+test( 'Position right', function() {
+ expect(4);
+
+ // We need this thing to be visible, so append it to the DOM
+ var origText = 'This is a really long random string and there is no way it fits in 100 pixels.';
+ var $wrapper = createWrappedDiv( origText, '100px' );
+ $( 'body' ).append( $wrapper );
+ $wrapper.autoEllipsis( { position: 'right' } );
+
+ // Verify that, and only one, span element was created
+ var $span = $wrapper.find( '> span' );
+ strictEqual( $span.length, 1, 'autoEllipsis wrapped the contents in a span element' );
+
+ // Check that the text fits by turning on word wrapping
+ $span.css( 'whiteSpace', 'nowrap' );
+ ltOrEq( $span.width(), $span.parent().width(), "Text fits (making the span 'white-space:nowrap' does not make it wider than its parent)" );
+
+ // Add two characters using scary black magic
+ var spanText = $span.text();
+ var d = findDivergenceIndex( origText, spanText );
+ var spanTextNew = spanText.substr( 0, d ) + origText[d] + origText[d] + '...';
+
+ gt( spanTextNew.length, spanText.length, 'Verify that the new span-length is indeed greater' );
+
+ // Put this text in the span and verify it doesn't fit
+ $span.text( spanTextNew );
+ // In IE6 width works like min-width, allow IE6's width to be "equal to"
+ if ( $.browser.msie && Number( $.browser.version ) == 6 ) {
+ gtOrEq( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more) - IE6: Maybe equal to as well due to width behaving like min-width in IE6' );
+ } else {
+ gt( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more)' );
+ }
+
+ // Clean up
+ $wrapper.remove();
+});
diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLength.js b/tests/qunit/suites/resources/jquery/jquery.byteLength.js
new file mode 100644
index 00000000..f82fda27
--- /dev/null
+++ b/tests/qunit/suites/resources/jquery/jquery.byteLength.js
@@ -0,0 +1,42 @@
+module( 'jquery.byteLength.js' );
+
+test( '-- Initial check', function() {
+ expect(1);
+ ok( $.byteLength, 'jQuery.byteLength defined' );
+} );
+
+test( 'Simple text', function() {
+ expect(5);
+
+ var azLc = 'abcdefghijklmnopqrstuvwxyz',
+ azUc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ num = '0123456789',
+ x = '*',
+ space = ' ';
+
+ equal( $.byteLength( azLc ), 26, 'Lowercase a-z' );
+ equal( $.byteLength( azUc ), 26, 'Uppercase A-Z' );
+ equal( $.byteLength( num ), 10, 'Numbers 0-9' );
+ equal( $.byteLength( x ), 1, 'An asterisk' );
+ equal( $.byteLength( space ), 3, '3 spaces' );
+
+} );
+
+test( 'Special text', window.foo = function() {
+ expect(5);
+
+ // http://en.wikipedia.org/wiki/UTF-8
+ var U_0024 = '\u0024',
+ U_00A2 = '\u00A2',
+ U_20AC = '\u20AC',
+ U_024B62 = '\u024B62',
+ // The normal one doesn't display properly, try the below which is the same
+ // according to http://www.fileformat.info/info/unicode/char/24B62/index.htm
+ U_024B62_alt = '\uD852\uDF62';
+
+ strictEqual( $.byteLength( U_0024 ), 1, 'U+0024: 1 byte. \u0024 (dollar sign)' );
+ strictEqual( $.byteLength( U_00A2 ), 2, 'U+00A2: 2 bytes. \u00A2 (cent sign)' );
+ strictEqual( $.byteLength( U_20AC ), 3, 'U+20AC: 3 bytes. \u20AC (euro sign)' );
+ strictEqual( $.byteLength( U_024B62 ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character)' );
+ strictEqual( $.byteLength( U_024B62_alt ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character) - alternative method' );
+} );
diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLimit.js b/tests/qunit/suites/resources/jquery/jquery.byteLimit.js
new file mode 100644
index 00000000..461ea49b
--- /dev/null
+++ b/tests/qunit/suites/resources/jquery/jquery.byteLimit.js
@@ -0,0 +1,155 @@
+module( 'jquery.byteLimit.js' );
+
+test( '-- Initial check', function() {
+ expect(1);
+ ok( $.fn.byteLimit, 'jQuery.fn.byteLimit defined' );
+} );
+
+// Basic sendkey-implementation
+$.addChars = function( $input, charstr ) {
+ var len = charstr.length;
+ for ( var i = 0; i < len; i++ ) {
+ // Keep track of the previous value
+ var prevVal = $input.val();
+
+ // Get the key code
+ var code = charstr.charCodeAt(i);
+
+ // Trigger event and undo if prevented
+ var event = new jQuery.Event( 'keypress', { keyCode: code, which: code, charCode: code } );
+ $input.trigger( event );
+ if ( !event.isDefaultPrevented() ) {
+ $input.val( prevVal + charstr.charAt(i) );
+ }
+ }
+};
+var blti = 0;
+/**
+ * Test factory for $.fn.byteLimit
+ *
+ * @param $input {jQuery} jQuery object in an input element
+ * @param useLimit {Boolean} Wether a limit should apply at all
+ * @param limit {Number} Limit (if used) otherwise undefined
+ * The limit should be less than 20 (the sample data's length)
+ */
+var byteLimitTest = function( options ) {
+ var opt = $.extend({
+ description: '',
+ $input: null,
+ sample: '',
+ useLimit: false,
+ expected: 0,
+ limit: null
+ }, options);
+ var i = blti++;
+
+ test( opt.description, function() {
+
+ opt.$input.appendTo( 'body' );
+
+ // Simulate pressing keys for each of the sample characters
+ $.addChars( opt.$input, opt.sample );
+ var newVal = opt.$input.val();
+
+ if ( opt.useLimit ) {
+ expect(2);
+
+ ltOrEq( $.byteLength( newVal ), opt.limit, 'Prevent keypresses after byteLimit was reached, length never exceeded the limit' );
+ equal( $.byteLength( newVal ), opt.expected, 'Not preventing keypresses too early, length has reached the expected length' );
+
+ } else {
+ expect(1);
+ equal( $.byteLength( newVal ), opt.expected, 'Unlimited scenarios are not affected, expected length reached' );
+ }
+
+ opt.$input.remove();
+ } );
+};
+
+var
+ // Simple sample (20 chars, 20 bytes)
+ simpleSample = '12345678901234567890',
+
+ // 3 bytes (euro-symbol)
+ U_20AC = '\u20AC',
+
+ // Multi-byte sample (22 chars, 26 bytes)
+ mbSample = '1234567890' + U_20AC + '1234567890' + U_20AC;
+
+byteLimitTest({
+ description: 'Plain text input',
+ $input: $( '<input>' )
+ .attr( {
+ 'type': 'text'
+ }),
+ sample: simpleSample,
+ useLimit: false,
+ expected: $.byteLength( simpleSample )
+});
+
+byteLimitTest({
+ description: 'Limit using the maxlength attribute',
+ $input: $( '<input>' )
+ .attr( {
+ 'type': 'text',
+ 'maxlength': '10'
+ })
+ .byteLimit(),
+ sample: simpleSample,
+ useLimit: true,
+ limit: 10,
+ expected: 10
+});
+
+byteLimitTest({
+ description: 'Limit using a custom value',
+ $input: $( '<input>' )
+ .attr( {
+ 'type': 'text'
+ })
+ .byteLimit( 10 ),
+ sample: simpleSample,
+ useLimit: true,
+ limit: 10,
+ expected: 10
+});
+
+byteLimitTest({
+ description: 'Limit using a custom value, overriding maxlength attribute',
+ $input: $( '<input>' )
+ .attr( {
+ 'type': 'text',
+ 'maxLength': '10'
+ })
+ .byteLimit( 15 ),
+ sample: simpleSample,
+ useLimit: true,
+ limit: 15,
+ expected: 15
+});
+
+byteLimitTest({
+ description: 'Limit using a custom value (multibyte)',
+ $input: $( '<input>' )
+ .attr( {
+ 'type': 'text'
+ })
+ .byteLimit( 14 ),
+ sample: mbSample,
+ useLimit: true,
+ limit: 14,
+ expected: 14 // (10 x 1-byte char) + (1 x 3-byte char) + (1 x 1-byte char)
+});
+
+byteLimitTest({
+ description: 'Limit using a custom value (multibyte) overlapping a byte',
+ $input: $( '<input>' )
+ .attr( {
+ 'type': 'text'
+ })
+ .byteLimit( 12 ),
+ sample: mbSample,
+ useLimit: true,
+ limit: 12,
+ expected: 12 // 10 x 1-byte char. The next 3-byte char exceeds limit of 12, but 2 more 1-byte chars come in after.
+});
diff --git a/tests/qunit/suites/resources/jquery/jquery.client.js b/tests/qunit/suites/resources/jquery/jquery.client.js
new file mode 100644
index 00000000..50df2928
--- /dev/null
+++ b/tests/qunit/suites/resources/jquery/jquery.client.js
@@ -0,0 +1,205 @@
+module( 'jquery.client.js' );
+
+test( '-- Initial check', function() {
+ expect(1);
+ ok( jQuery.client, 'jQuery.client defined' );
+});
+
+test( 'profile userAgent support', function() {
+ expect(8);
+
+ // Object keyed by userAgent. Value is an array (human-readable name, client-profile object, navigator.platform value)
+ // Info based on results from http://toolserver.org/~krinkle/testswarm/job/174/
+ var uas = {
+ // Internet Explorer 6
+ // Internet Explorer 7
+ 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)': {
+ title: 'Internet Explorer 7',
+ platform: 'Win32',
+ profile: {
+ "name": "msie",
+ "layout": "trident",
+ "layoutVersion": "unknown",
+ "platform": "win",
+ "version": "7.0",
+ "versionBase": "7",
+ "versionNumber": 7
+ }
+ },
+ // Internet Explorer 8
+ // Internet Explorer 9
+ // Internet Explorer 10
+ // Firefox 2
+ // Firefox 3.5
+ 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.19) Gecko/20110420 Firefox/3.5.19': {
+ title: 'Firefox 3.5',
+ platform: 'MacIntel',
+ profile: {
+ "name": "firefox",
+ "layout": "gecko",
+ "layoutVersion": 20110420,
+ "platform": "mac",
+ "version": "3.5.19",
+ "versionBase": "3",
+ "versionNumber": 3.5
+ }
+ },
+ // Firefox 3.6
+ 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.17) Gecko/20110422 Ubuntu/10.10 (maverick) Firefox/3.6.17': {
+ title: 'Firefox 3.6',
+ platform: 'Linux i686',
+ profile: {
+ "name": "firefox",
+ "layout": "gecko",
+ "layoutVersion": 20110422,
+ "platform": "linux",
+ "version": "3.6.17",
+ "versionBase": "3",
+ "versionNumber": 3.6
+ }
+ },
+ // Firefox 4
+ 'Mozilla/5.0 (Windows NT 6.0; rv:2.0.1) Gecko/20100101 Firefox/4.0.1': {
+ title: 'Firefox 4',
+ platform: 'Win32',
+ profile: {
+ "name": "firefox",
+ "layout": "gecko",
+ "layoutVersion": 20100101,
+ "platform": "win",
+ "version": "4.0.1",
+ "versionBase": "4",
+ "versionNumber": 4
+ }
+ },
+ // Firefox 5
+ // Safari 3
+ // Safari 4
+ 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; nl-nl) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': {
+ title: 'Safari 4',
+ platform: 'MacIntel',
+ profile: {
+ "name": "safari",
+ "layout": "webkit",
+ "layoutVersion": 531,
+ "platform": "mac",
+ "version": "4.0.5",
+ "versionBase": "4",
+ "versionNumber": 4
+ }
+ },
+ 'Mozilla/5.0 (Windows; U; Windows NT 6.0; cs-CZ) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': {
+ title: 'Safari 4',
+ platform: 'Win32',
+ profile: {
+ "name": "safari",
+ "layout": "webkit",
+ "layoutVersion": 533,
+ "platform": "win",
+ "version": "4.0.5",
+ "versionBase": "4",
+ "versionNumber": 4
+ }
+ },
+ // Safari 5
+ // Opera 10
+ // Chrome 5
+ // Chrome 6
+ // Chrome 7
+ // Chrome 8
+ // Chrome 9
+ // Chrome 10
+ // Chrome 11
+ // Chrome 12
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30': {
+ title: 'Chrome 12',
+ platform: 'MacIntel',
+ profile: {
+ "name": "chrome",
+ "layout": "webkit",
+ "layoutVersion": 534,
+ "platform": "mac",
+ "version": "12.0.742.112",
+ "versionBase": "12",
+ "versionNumber": 12
+ }
+ },
+ 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.68 Safari/534.30': {
+ title: 'Chrome 12',
+ platform: 'Linux i686',
+ profile: {
+ "name": "chrome",
+ "layout": "webkit",
+ "layoutVersion": 534,
+ "platform": "linux",
+ "version": "12.0.742.68",
+ "versionBase": "12",
+ "versionNumber": 12
+ }
+ }
+ };
+
+ // Generate a client profile object and compare recursively
+ var uaTest = function( rawUserAgent, data ) {
+ var ret = $.client.profile( {
+ userAgent: rawUserAgent,
+ platform: data.platform
+ } );
+ deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent );
+ };
+
+ // Loop through and run tests
+ $.each( uas, uaTest );
+} );
+
+test( 'profile return validation for current user agent', function() {
+ expect(7);
+ var p = $.client.profile();
+ var unknownOrType = function( val, type, summary ) {
+ return ok( typeof val === type || val === 'unknown', summary );
+ };
+
+ equal( typeof p, 'object', 'profile returns an object' );
+ unknownOrType( p.layout, 'string', 'p.layout is a string (or "unknown")' );
+ unknownOrType( p.layoutVersion, 'number', 'p.layoutVersion is a number (or "unknown")' );
+ unknownOrType( p.platform, 'string', 'p.platform is a string (or "unknown")' );
+ unknownOrType( p.version, 'string', 'p.version is a string (or "unknown")' );
+ unknownOrType( p.versionBase, 'string', 'p.versionBase is a string (or "unknown")' );
+ equal( typeof p.versionNumber, 'number', 'p.versionNumber is a number' );
+});
+
+test( 'test', function() {
+ expect(1);
+
+ // Example from WikiEditor
+ var testMap = {
+ 'ltr': {
+ 'msie': [['>=', 7]],
+ 'firefox': [['>=', 2]],
+ 'opera': [['>=', 9.6]],
+ 'safari': [['>=', 3]],
+ 'chrome': [['>=', 3]],
+ 'netscape': [['>=', 9]],
+ 'blackberry': false,
+ 'ipod': false,
+ 'iphone': false
+ },
+ 'rtl': {
+ 'msie': [['>=', 8]],
+ 'firefox': [['>=', 2]],
+ 'opera': [['>=', 9.6]],
+ 'safari': [['>=', 3]],
+ 'chrome': [['>=', 3]],
+ 'netscape': [['>=', 9]],
+ 'blackberry': false,
+ 'ipod': false,
+ 'iphone': false
+ }
+ };
+ // .test() uses eval, make sure no exceptions are thrown
+ // then do a basic return value type check
+ var testMatch = $.client.test( testMap );
+
+ equal( typeof testMatch, 'boolean', 'test returns a boolean value' );
+
+});
diff --git a/tests/qunit/suites/resources/jquery/jquery.colorUtil.js b/tests/qunit/suites/resources/jquery/jquery.colorUtil.js
new file mode 100644
index 00000000..93f12b82
--- /dev/null
+++ b/tests/qunit/suites/resources/jquery/jquery.colorUtil.js
@@ -0,0 +1,71 @@
+module( 'jquery.colorUtil.js' );
+
+test( '-- Initial check', function() {
+ expect(1);
+ ok( $.colorUtil, '$.colorUtil defined' );
+});
+
+test( 'getRGB', function() {
+ expect(18);
+
+ strictEqual( $.colorUtil.getRGB(), undefined, 'No arguments' );
+ strictEqual( $.colorUtil.getRGB( '' ), undefined, 'Empty string' );
+ deepEqual( $.colorUtil.getRGB( [0, 100, 255] ), [0, 100, 255], 'Parse array of rgb values' );
+ deepEqual( $.colorUtil.getRGB( 'rgb(0,100,255)' ), [0, 100, 255], 'Parse simple rgb string' );
+ deepEqual( $.colorUtil.getRGB( 'rgb(0, 100, 255)' ), [0, 100, 255], 'Parse simple rgb string with spaces' );
+ deepEqual( $.colorUtil.getRGB( 'rgb(0%,20%,40%)' ), [0, 51, 102], 'Parse rgb string with percentages' );
+ deepEqual( $.colorUtil.getRGB( 'rgb(0%, 20%, 40%)' ), [0, 51, 102], 'Parse rgb string with percentages and spaces' );
+ deepEqual( $.colorUtil.getRGB( '#f2ddee' ), [242, 221, 238], 'Hex string: 6 char lowercase' );
+ deepEqual( $.colorUtil.getRGB( '#f2DDEE' ), [242, 221, 238], 'Hex string: 6 char uppercase' );
+ deepEqual( $.colorUtil.getRGB( '#f2DdEe' ), [242, 221, 238], 'Hex string: 6 char mixed' );
+ deepEqual( $.colorUtil.getRGB( '#eee' ), [238, 238, 238], 'Hex string: 3 char lowercase' );
+ deepEqual( $.colorUtil.getRGB( '#EEE' ), [238, 238, 238], 'Hex string: 3 char uppercase' );
+ deepEqual( $.colorUtil.getRGB( '#eEe' ), [238, 238, 238], 'Hex string: 3 char mixed' );
+ deepEqual( $.colorUtil.getRGB( 'rgba(0, 0, 0, 0)' ), [255, 255, 255], 'Zero rgba for Safari 3; Transparent (whitespace)' );
+
+ // Perhaps this is a bug in colorUtil, but it is the current behaviour so, let's keep
+ // track of it, so we will know in case it would ever change.
+ strictEqual( $.colorUtil.getRGB( 'rgba(0,0,0,0)' ), undefined, 'Zero rgba without whitespace' );
+
+ deepEqual( $.colorUtil.getRGB( 'lightGreen' ), [144, 238, 144], 'Color names (lightGreen)' );
+ deepEqual( $.colorUtil.getRGB( 'transparent' ), [255, 255, 255], 'Color names (transparent)' );
+ strictEqual( $.colorUtil.getRGB( 'mediaWiki' ), undefined, 'Inexisting color name' );
+});
+
+test( 'rgbToHsl', function() {
+ expect(1);
+
+ var hsl = $.colorUtil.rgbToHsl( 144, 238, 144 );
+
+ // Cross-browser differences in decimals...
+ // Round to two decimals so they can be more reliably checked.
+ var dualDecimals = function(a,b){
+ return Math.round(a*100)/100;
+ };
+ // Re-create the rgbToHsl return array items, limited to two decimals.
+ var ret = [dualDecimals(hsl[0]), dualDecimals(hsl[1]), dualDecimals(hsl[2])];
+
+ deepEqual( ret, [0.33, 0.73, 0.75], 'rgb(144, 238, 144): hsl(0.33, 0.73, 0.75)' );
+});
+
+test( 'hslToRgb', function() {
+ expect(1);
+
+ var rgb = $.colorUtil.hslToRgb( 0.3, 0.7, 0.8 );
+
+ // Cross-browser differences in decimals...
+ // Re-create the hslToRgb return array items, rounded to whole numbers.
+ var ret = [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2])];
+
+ deepEqual( ret ,[183, 240, 168], 'hsl(0.3, 0.7, 0.8): rgb(183, 240, 168)' );
+});
+
+test( 'getColorBrightness', function() {
+ expect(2);
+
+ var a = $.colorUtil.getColorBrightness( 'red', +0.1 );
+ equal( a, 'rgb(255,50,50)', 'Start with named color "red", brighten 10%' );
+
+ var b = $.colorUtil.getColorBrightness( 'rgb(200,50,50)', -0.2 );
+ equal( b, 'rgb(118,29,29)', 'Start with rgb string "rgb(200,50,50)", darken 20%' );
+});
diff --git a/tests/qunit/suites/resources/jquery/jquery.getAttrs.js b/tests/qunit/suites/resources/jquery/jquery.getAttrs.js
new file mode 100644
index 00000000..3d3d01e1
--- /dev/null
+++ b/tests/qunit/suites/resources/jquery/jquery.getAttrs.js
@@ -0,0 +1,17 @@
+module( 'jquery.getAttrs.js' );
+
+test( '-- Initial check', function() {
+ expect(1);
+ ok( $.fn.getAttrs, 'jQuery.fn.getAttrs defined' );
+} );
+
+test( 'Check', function() {
+ expect(1);
+ var attrs = {
+ foo: 'bar',
+ 'class': 'lorem'
+ },
+ $el = $( '<div>', attrs );
+
+ deepEqual( $el.getAttrs(), attrs, 'getAttrs() return object should match the attributes set, no more, no less' );
+} );
diff --git a/tests/qunit/suites/resources/jquery/jquery.localize.js b/tests/qunit/suites/resources/jquery/jquery.localize.js
new file mode 100644
index 00000000..40b58687
--- /dev/null
+++ b/tests/qunit/suites/resources/jquery/jquery.localize.js
@@ -0,0 +1,119 @@
+module( 'jquery.localize.js' );
+
+test( '-- Initial check', function() {
+ expect(1);
+ ok( $.fn.localize, 'jQuery.fn.localize defined' );
+} );
+
+test( 'Handle basic replacements', function() {
+ expect(3);
+
+ var html, $lc;
+ mw.messages.set( 'basic', 'Basic stuff' );
+
+ // Tag: html:msg
+ html = '<div><span><html:msg key="basic" /></span></div>';
+ $lc = $( html ).localize().find( 'span' );
+
+ strictEqual( $lc.text(), 'Basic stuff', 'Tag: html:msg' );
+
+ // Attribute: title-msg
+ html = '<div><span title-msg="basic" /></span></div>';
+ $lc = $( html ).localize().find( 'span' );
+
+ strictEqual( $lc.attr( 'title' ), 'Basic stuff', 'Attribute: title-msg' );
+
+ // Attribute: alt-msg
+ html = '<div><span alt-msg="basic" /></span></div>';
+ $lc = $( html ).localize().find( 'span' );
+
+ strictEqual( $lc.attr( 'alt' ), 'Basic stuff', 'Attribute: alt-msg' );
+} );
+
+test( 'Proper escaping', function() {
+ expect(2);
+
+ var html, $lc;
+ mw.messages.set( 'properfoo', '<proper esc="test">' );
+
+ // This is handled by jQuery inside $.fn.localize, just a simple sanity checked
+ // making sure it is actually using text() and attr() (or something with the same effect)
+
+ // Text escaping
+ html = '<div><span><html:msg key="properfoo" /></span></div>';
+ $lc = $( html ).localize().find( 'span' );
+
+ strictEqual( $lc.text(), mw.msg( 'properfoo' ), 'Content is inserted as text, not as html.' );
+
+ // Attribute escaping
+ html = '<div><span title-msg="properfoo" /></span></div>';
+ $lc = $( html ).localize().find( 'span' );
+
+ strictEqual( $lc.attr( 'title' ), mw.msg( 'properfoo' ), 'Attributes are not inserted raw.' );
+} );
+
+test( 'Options', function() {
+ expect(7);
+
+ mw.messages.set( {
+ 'foo-lorem': 'Lorem',
+ 'foo-ipsum': 'Ipsum',
+ 'foo-bar-title': 'Read more about bars',
+ 'foo-bar-label': 'The Bars',
+ 'foo-bazz-title': 'Read more about bazz at $1 (last modified: $2)',
+ 'foo-bazz-label': 'The Bazz ($1)',
+ 'foo-welcome': 'Welcome to $1! (last visit: $2)'
+ } );
+ var html, $lc, attrs, x, sitename = 'Wikipedia';
+
+ // Message key prefix
+ html = '<div><span title-msg="lorem"><html:msg key="ipsum" /></span></div>';
+ $lc = $( html ).localize( {
+ prefix: 'foo-'
+ } ).find( 'span' );
+
+ strictEqual( $lc.attr( 'title' ), 'Lorem', 'Message key prefix - attr' );
+ strictEqual( $lc.text(), 'Ipsum', 'Message key prefix - text' );
+
+ // Variable keys mapping
+ x = 'bar';
+ html = '<div><span title-msg="title"><html:msg key="label" /></span></div>';
+ $lc = $( html ).localize( {
+ keys: {
+ 'title': 'foo-' + x + '-title',
+ 'label': 'foo-' + x + '-label'
+ }
+ } ).find( 'span' );
+
+ strictEqual( $lc.attr( 'title' ), 'Read more about bars', 'Variable keys mapping - attr' );
+ strictEqual( $lc.text(), 'The Bars', 'Variable keys mapping - text' );
+
+ // Passing parameteters to mw.msg
+ html = '<div><span><html:msg key="foo-welcome" /></span></div>';
+ $lc = $( html ).localize( {
+ params: {
+ 'foo-welcome': [sitename, 'yesterday']
+ }
+ } ).find( 'span' );
+
+ strictEqual( $lc.text(), 'Welcome to Wikipedia! (last visit: yesterday)', 'Passing parameteters to mw.msg' );
+
+ // Combination of options prefix, params and keys
+ x = 'bazz';
+ html = '<div><span title-msg="title"><html:msg key="label" /></span></div>';
+ $lc = $( html ).localize( {
+ prefix: 'foo-',
+ keys: {
+ 'title': x + '-title',
+ 'label': x + '-label'
+ },
+ params: {
+ 'title': [sitename, '3 minutes ago'],
+ 'label': [sitename, '3 minutes ago']
+
+ }
+ } ).find( 'span' );
+
+ strictEqual( $lc.text(), 'The Bazz (Wikipedia)', 'Combination of options prefix, params and keys - text' );
+ strictEqual( $lc.attr( 'title' ), 'Read more about bazz at Wikipedia (last modified: 3 minutes ago)', 'Combination of options prefix, params and keys - attr' );
+} );
diff --git a/tests/qunit/suites/resources/jquery/jquery.mwPrototypes.js b/tests/qunit/suites/resources/jquery/jquery.mwPrototypes.js
new file mode 100644
index 00000000..bb6d2a1b
--- /dev/null
+++ b/tests/qunit/suites/resources/jquery/jquery.mwPrototypes.js
@@ -0,0 +1,56 @@
+module( 'jquery.mwPrototypes.js' );
+
+test( 'String functions', function() {
+
+ equal( $.trimLeft( ' foo bar ' ), 'foo bar ', 'trimLeft' );
+ equal( $.trimRight( ' foo bar ' ), ' foo bar', 'trimRight' );
+ equal( $.ucFirst( 'foo'), 'Foo', 'ucFirst' );
+
+ equal( $.escapeRE( '<!-- ([{+mW+}]) $^|?>' ),
+ '<!\\-\\- \\(\\[\\{\\+mW\\+\\}\\]\\) \\$\\^\\|\\?>', 'escapeRE - Escape specials' );
+ equal( $.escapeRE( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ),
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'escapeRE - Leave uppercase alone' );
+ equal( $.escapeRE( 'abcdefghijklmnopqrstuvwxyz' ),
+ 'abcdefghijklmnopqrstuvwxyz', 'escapeRE - Leave lowercase alone' );
+ equal( $.escapeRE( '0123456789' ), '0123456789', 'escapeRE - Leave numbers alone' );
+});
+
+test( 'Is functions', function() {
+
+ strictEqual( $.isDomElement( document.getElementById( 'qunit-header' ) ), true,
+ 'isDomElement: #qunit-header Node' );
+ strictEqual( $.isDomElement( document.getElementById( 'random-name' ) ), false,
+ 'isDomElement: #random-name (null)' );
+ strictEqual( $.isDomElement( document.getElementsByTagName( 'div' ) ), false,
+ 'isDomElement: getElementsByTagName Array' );
+ strictEqual( $.isDomElement( document.getElementsByTagName( 'div' )[0] ), true,
+ 'isDomElement: getElementsByTagName(..)[0] Node' );
+ strictEqual( $.isDomElement( $( 'div' ) ), false,
+ 'isDomElement: jQuery object' );
+ strictEqual( $.isDomElement( $( 'div' ).get(0) ), true,
+ 'isDomElement: jQuery object > Get node' );
+ strictEqual( $.isDomElement( document.createElement( 'div' ) ), true,
+ 'isDomElement: createElement' );
+ strictEqual( $.isDomElement( { foo: 1 } ), false,
+ 'isDomElement: Object' );
+
+ strictEqual( $.isEmpty( 'string' ), false, 'isEmptry: "string"' );
+ strictEqual( $.isEmpty( '0' ), true, 'isEmptry: "0"' );
+ strictEqual( $.isEmpty( [] ), true, 'isEmptry: []' );
+ strictEqual( $.isEmpty( {} ), true, 'isEmptry: {}' );
+
+ // Documented behaviour
+ strictEqual( $.isEmpty( { length: 0 } ), true, 'isEmptry: { length: 0 }' );
+});
+
+test( 'Comparison functions', function() {
+
+ ok( $.compareArray( [0, 'a', [], [2, 'b'] ], [0, "a", [], [2, "b"] ] ),
+ 'compareArray: Two deep arrays that are excactly the same' );
+ ok( !$.compareArray( [1], [2] ), 'compareArray: Two different arrays (false)' );
+
+ ok( $.compareObject( {}, {} ), 'compareObject: Two empty objects' );
+ ok( $.compareObject( { foo: 1 }, { foo: 1 } ), 'compareObject: Two the same objects' );
+ ok( !$.compareObject( { bar: true }, { baz: false } ),
+ 'compareObject: Two different objects (false)' );
+});
diff --git a/tests/qunit/suites/resources/jquery/jquery.tabIndex.js b/tests/qunit/suites/resources/jquery/jquery.tabIndex.js
new file mode 100644
index 00000000..1ff81e58
--- /dev/null
+++ b/tests/qunit/suites/resources/jquery/jquery.tabIndex.js
@@ -0,0 +1,50 @@
+module( 'jquery.tabIndex.js' );
+
+test( '-- Initial check', function() {
+ expect(2);
+
+ ok( $.fn.firstTabIndex, '$.fn.firstTabIndex defined' );
+ ok( $.fn.lastTabIndex, '$.fn.lastTabIndex defined' );
+});
+
+test( 'firstTabIndex', function() {
+ expect(2);
+
+ var testEnvironment =
+'<form>' +
+ '<input tabindex="7" />' +
+ '<input tabindex="9" />' +
+ '<textarea tabindex="2">Foobar</textarea>' +
+ '<textarea tabindex="5">Foobar</textarea>' +
+'</form>';
+
+ var $testA = $( '<div>' ).html( testEnvironment ).appendTo( 'body' );
+ strictEqual( $testA.firstTabIndex(), 2, 'First tabindex should be 2 within this context.' );
+
+ var $testB = $( '<div>' );
+ strictEqual( $testB.firstTabIndex(), null, 'Return null if none available.' );
+
+ // Clean up
+ $testA.add( $testB ).remove();
+});
+
+test( 'lastTabIndex', function() {
+ expect(2);
+
+ var testEnvironment =
+'<form>' +
+ '<input tabindex="7" />' +
+ '<input tabindex="9" />' +
+ '<textarea tabindex="2">Foobar</textarea>' +
+ '<textarea tabindex="5">Foobar</textarea>' +
+'</form>';
+
+ var $testA = $( '<div>' ).html( testEnvironment ).appendTo( 'body' );
+ strictEqual( $testA.lastTabIndex(), 9, 'Last tabindex should be 9 within this context.' );
+
+ var $testB = $( '<div>' );
+ strictEqual( $testB.lastTabIndex(), null, 'Return null if none available.' );
+
+ // Clean up
+ $testA.add( $testB ).remove();
+});
diff --git a/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js b/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
new file mode 100644
index 00000000..f47b7f40
--- /dev/null
+++ b/tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
@@ -0,0 +1,475 @@
+(function() {
+
+module( 'jquery.tablesorter.test.js' );
+
+// setup hack
+mw.config.set('wgMonthNames', window.wgMonthNames = ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']);
+mw.config.set('wgMonthNamesShort', window.wgMonthNamesShort = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']);
+mw.config.set('wgDefaultDateFormat', window.wgDefaultDateFormat = 'dmy');
+
+test( '-- Initial check', function() {
+ expect(1);
+ ok( $.tablesorter, '$.tablesorter defined' );
+});
+
+/**
+ * Create an HTML table from an array of row arrays containing text strings.
+ * First row will be header row. No fancy rowspan/colspan stuff.
+ *
+ * @param {String[]} header
+ * @param {String[][]} data
+ * @return jQuery
+ */
+var tableCreate = function( header, data ) {
+ var $table = $('<table class="sortable"><thead></thead><tbody></tbody></table>'),
+ $thead = $table.find('thead'),
+ $tbody = $table.find('tbody');
+ var $tr = $('<tr>');
+ $.each(header, function(i, str) {
+ var $th = $('<th>');
+ $th.text(str).appendTo($tr);
+ });
+ $tr.appendTo($thead);
+
+ for (var i = 0; i < data.length; i++) {
+ $tr = $('<tr>');
+ $.each(data[i], function(j, str) {
+ var $td = $('<td>');
+ $td.text(str).appendTo($tr);
+ });
+ $tr.appendTo($tbody);
+ }
+ return $table;
+};
+
+/**
+ * Extract text from table.
+ *
+ * @param {jQuery} $table
+ * @return String[][]
+ */
+var tableExtract = function( $table ) {
+ var data = [];
+ $table.find('tbody').find('tr').each(function(i, tr) {
+ var row = [];
+ $(tr).find('td,th').each(function(i, td) {
+ row.push($(td).text());
+ });
+ data.push(row);
+ });
+ return data;
+};
+
+/**
+ * Run a table test by building a table with the given data,
+ * running some callback on it, then checking the results.
+ *
+ * @param {String} msg text to pass on to qunit for the comparison
+ * @param {String[]} header cols to make the table
+ * @param {String[][]} data rows/cols to make the table
+ * @param {String[][]} expected rows/cols to compare against at end
+ * @param {function($table)} callback something to do with the table before we compare
+ */
+var tableTest = function( msg, header, data, expected, callback ) {
+ test( msg, function() {
+ expect(1);
+
+ var $table = tableCreate( header, data );
+ //$('body').append($table);
+
+ // Give caller a chance to set up sorting and manipulate the table.
+ callback( $table );
+
+ // Table sorting is done synchronously; if it ever needs to change back
+ // to asynchronous, we'll need a timeout or a callback here.
+ var extracted = tableExtract( $table );
+ deepEqual( extracted, expected, msg );
+ });
+};
+
+var reversed = function(arr) {
+ var arr2 = arr.slice(0);
+ arr2.reverse();
+ return arr2;
+};
+
+// Sample data set: some planets!
+var header = ['Planet', 'Radius (km)'],
+ mercury = ['Mercury', '2439.7'],
+ venus = ['Venus', '6051.8'],
+ earth = ['Earth', '6371.0'],
+ mars = ['Mars', '3390.0'],
+ jupiter = ['Jupiter', '69911'],
+ saturn = ['Saturn', '58232'];
+
+// Initial data set
+var planets = [mercury, venus, earth, mars, jupiter, saturn];
+var ascendingName = [earth, jupiter, mars, mercury, saturn, venus];
+var ascendingRadius = [mercury, mars, venus, earth, saturn, jupiter];
+
+tableTest(
+ 'Basic planet table: ascending by name',
+ header,
+ planets,
+ ascendingName,
+ function( $table ) {
+ $table.tablesorter();
+ $table.find('.headerSort:eq(0)').click();
+ }
+);
+tableTest(
+ 'Basic planet table: ascending by name a second time',
+ header,
+ planets,
+ ascendingName,
+ function( $table ) {
+ $table.tablesorter();
+ $table.find('.headerSort:eq(0)').click();
+ }
+);
+tableTest(
+ 'Basic planet table: descending by name',
+ header,
+ planets,
+ reversed(ascendingName),
+ function( $table ) {
+ $table.tablesorter();
+ $table.find('.headerSort:eq(0)').click().click();
+ }
+);
+tableTest(
+ 'Basic planet table: ascending radius',
+ header,
+ planets,
+ ascendingRadius,
+ function( $table ) {
+ $table.tablesorter();
+ $table.find('.headerSort:eq(1)').click();
+ }
+);
+tableTest(
+ 'Basic planet table: descending radius',
+ header,
+ planets,
+ reversed(ascendingRadius),
+ function( $table ) {
+ $table.tablesorter();
+ $table.find('.headerSort:eq(1)').click().click();
+ }
+);
+
+
+// Regression tests!
+tableTest(
+ 'Bug 28775: German-style short numeric dates',
+ ['Date'],
+ [
+ // German-style dates are day-month-year
+ ['11.11.2011'],
+ ['01.11.2011'],
+ ['02.10.2011'],
+ ['03.08.2011'],
+ ['09.11.2011']
+ ],
+ [
+ // Sorted by ascending date
+ ['03.08.2011'],
+ ['02.10.2011'],
+ ['01.11.2011'],
+ ['09.11.2011'],
+ ['11.11.2011']
+ ],
+ function( $table ) {
+ // @fixme reset it at end or change module to allow us to override it
+ mw.config.set('wgDefaultDateFormat', window.wgDefaultDateFormat = 'dmy');
+ $table.tablesorter();
+ $table.find('.headerSort:eq(0)').click();
+ }
+);
+tableTest(
+ 'Bug 28775: American-style short numeric dates',
+ ['Date'],
+ [
+ // American-style dates are month-day-year
+ ['11.11.2011'],
+ ['01.11.2011'],
+ ['02.10.2011'],
+ ['03.08.2011'],
+ ['09.11.2011']
+ ],
+ [
+ // Sorted by ascending date
+ ['01.11.2011'],
+ ['02.10.2011'],
+ ['03.08.2011'],
+ ['09.11.2011'],
+ ['11.11.2011']
+ ],
+ function( $table ) {
+ // @fixme reset it at end or change module to allow us to override it
+ mw.config.set('wgDefaultDateFormat', window.wgDefaultDateFormat = 'mdy');
+ $table.tablesorter();
+ $table.find('.headerSort:eq(0)').click();
+ }
+);
+
+var ipv4 = [
+ // Some randomly generated fake IPs
+ ['45.238.27.109'],
+ ['44.172.9.22'],
+ ['247.240.82.209'],
+ ['204.204.132.158'],
+ ['170.38.91.162'],
+ ['197.219.164.9'],
+ ['45.68.154.72'],
+ ['182.195.149.80']
+];
+var ipv4Sorted = [
+ // Sort order should go octet by octet
+ ['44.172.9.22'],
+ ['45.68.154.72'],
+ ['45.238.27.109'],
+ ['170.38.91.162'],
+ ['182.195.149.80'],
+ ['197.219.164.9'],
+ ['204.204.132.158'],
+ ['247.240.82.209']
+];
+tableTest(
+ 'Bug 17141: IPv4 address sorting',
+ ['IP'],
+ ipv4,
+ ipv4Sorted,
+ function( $table ) {
+ $table.tablesorter();
+ $table.find('.headerSort:eq(0)').click();
+ }
+);
+tableTest(
+ 'Bug 17141: IPv4 address sorting (reverse)',
+ ['IP'],
+ ipv4,
+ reversed(ipv4Sorted),
+ function( $table ) {
+ $table.tablesorter();
+ $table.find('.headerSort:eq(0)').click().click();
+ }
+);
+
+var umlautWords = [
+ // Some words with Umlauts
+ ['Günther'],
+ ['Peter'],
+ ['Björn'],
+ ['Bjorn'],
+ ['Apfel'],
+ ['Äpfel'],
+ ['Strasse'],
+ ['Sträßschen']
+];
+
+var umlautWordsSorted = [
+ // Some words with Umlauts
+ ['Äpfel'],
+ ['Apfel'],
+ ['Björn'],
+ ['Bjorn'],
+ ['Günther'],
+ ['Peter'],
+ ['Sträßschen'],
+ ['Strasse']
+];
+
+tableTest(
+ 'Accented Characters with custom collation',
+ ['Name'],
+ umlautWords,
+ umlautWordsSorted,
+ function( $table ) {
+ mw.config.set('tableSorterCollation', {'ä':'ae', 'ö' : 'oe', 'ß': 'ss', 'ü':'ue'});
+ $table.tablesorter();
+ $table.find('.headerSort:eq(0)').click();
+ mw.config.set('tableSorterCollation', {});
+ }
+);
+
+var planetsRowspan =[["Earth","6051.8"], jupiter, ["Mars","6051.8"], mercury, saturn, venus];
+var planetsRowspanII =[jupiter, mercury, saturn, ['Venus', '6371.0'], venus, ['Venus', '3390.0']];
+
+tableTest(
+ 'Basic planet table: Same value for multiple rows via rowspan',
+ header,
+ planets,
+ planetsRowspan,
+ function( $table ) {
+ //Quick&Dirty mod
+ $table.find('tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)').remove();
+ $table.find('tr:eq(2) td:eq(1)').attr('rowspan', '3');
+ $table.tablesorter();
+ $table.find('.headerSort:eq(0)').click();
+ }
+);
+tableTest(
+ 'Basic planet table: Same value for multiple rows via rowspan II',
+ header,
+ planets,
+ planetsRowspanII,
+ function( $table ) {
+ //Quick&Dirty mod
+ $table.find('tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)').remove();
+ $table.find('tr:eq(2) td:eq(0)').attr('rowspan', '3');
+ $table.tablesorter();
+ $table.find('.headerSort:eq(0)').click();
+ }
+);
+
+var complexMDYDates = [
+ // Some words with Umlauts
+ ['January, 19 2010'],
+ ['April 21 1991'],
+ ['04 22 1991'],
+ ['5.12.1990'],
+ ['December 12 \'10']
+];
+
+var complexMDYSorted = [
+ ["5.12.1990"],
+ ["April 21 1991"],
+ ["04 22 1991"],
+ ["January, 19 2010"],
+ ["December 12 '10"]
+];
+
+tableTest(
+ 'Complex date parsing I',
+ ['date'],
+ complexMDYDates,
+ complexMDYSorted,
+ function( $table ) {
+ mw.config.set('wgDefaultDateFormat', window.wgDefaultDateFormat = 'mdy');
+ $table.tablesorter();
+ $table.find('.headerSort:eq(0)').click();
+ }
+);
+
+var ascendingNameLegacy = ascendingName.slice(0);
+ascendingNameLegacy[4] = ascendingNameLegacy[5];
+ascendingNameLegacy.pop();
+
+tableTest(
+ 'Legacy compat with .sortbottom',
+ header,
+ planets,
+ ascendingNameLegacy,
+ function( $table ) {
+ $table.find('tr:last').addClass('sortbottom');
+ $table.tablesorter();
+ $table.find('.headerSort:eq(0)').click();
+ }
+);
+
+/** FIXME: the diff output is not very readeable. */
+test( 'bug 32047 - caption must be before thead', function() {
+ var $table;
+ $table = $(
+ '<table class="sortable">' +
+ '<caption>CAPTION</caption>' +
+ '<tr><th>THEAD</th></tr>' +
+ '<tr><td>A</td></tr>' +
+ '<tr><td>B</td></tr>' +
+ '<tr class="sortbottom"><td>TFOOT</td></tr>' +
+ '</table>'
+ );
+ $table.tablesorter();
+
+ equals(
+ $table.children( ).get( 0 ).nodeName,
+ 'CAPTION',
+ 'First element after <thead> must be <caption> (bug 32047)'
+ );
+});
+
+test( 'data-sort-value attribute, when available, should override sorting position', function() {
+ var $table, data;
+
+ // Simple example, one without data-sort-value which should be sorted at it's text.
+ $table = $(
+ '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
+ '<tbody>' +
+ '<tr><td>Cheetah</td></tr>' +
+ '<tr><td data-sort-value="Apple">Bird</td></tr>' +
+ '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
+ '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
+ '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
+ '</tbody></table>'
+ );
+ $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+
+ data = [];
+ $table.find( 'tbody > tr' ).each( function( i, tr ) {
+ $( tr ).find( 'td' ).each( function( i, td ) {
+ data.push( { data: $( td ).data( 'sort-value' ), text: $( td ).text() } );
+ });
+ });
+
+ deepEqual( data, [
+ {
+ "data": "Apple",
+ "text": "Bird"
+ }, {
+ "data": "Bananna",
+ "text": "Ferret"
+ }, {
+ "data": undefined,
+ "text": "Cheetah"
+ }, {
+ "data": "Cherry",
+ "text": "Dolphin"
+ }, {
+ "data": "Drupe",
+ "text": "Elephant"
+ }
+ ] );
+
+ // Another example
+ $table = $(
+ '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
+ '<tbody>' +
+ '<tr><td>D</td></tr>' +
+ '<tr><td data-sort-value="E">A</td></tr>' +
+ '<tr><td>B</td></tr>' +
+ '<tr><td>G</td></tr>' +
+ '<tr><td data-sort-value="F">C</td></tr>' +
+ '</tbody></table>'
+ );
+ $table.tablesorter().find( '.headerSort:eq(0)' ).click();
+
+ data = [];
+ $table.find( 'tbody > tr' ).each( function( i, tr ) {
+ $( tr ).find( 'td' ).each( function( i, td ) {
+ data.push( { data: $( td ).data( 'sort-value' ), text: $( td ).text() } );
+ });
+ });
+
+ deepEqual( data, [
+ {
+ "data": undefined,
+ "text": "B"
+ }, {
+ "data": undefined,
+ "text": "D"
+ }, {
+ "data": "E",
+ "text": "A"
+ }, {
+ "data": "F",
+ "text": "C"
+ }, {
+ "data": undefined,
+ "text": "G"
+ }
+ ] );
+
+});
+
+})();
diff --git a/tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.js b/tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.js
new file mode 100644
index 00000000..bcc9b96b
--- /dev/null
+++ b/tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.js
@@ -0,0 +1,71 @@
+module( 'mediawiki.special.recentchanges.js' );
+
+test( '-- Initial check', function() {
+ expect( 2 );
+ ok( mw.special.recentchanges.init,
+ 'mw.special.recentchanges.init defined'
+ );
+ ok( mw.special.recentchanges.updateCheckboxes,
+ 'mw.special.recentchanges.updateCheckboxes defined'
+ );
+ // TODO: verify checkboxes == [ 'nsassociated', 'nsinvert' ]
+});
+
+test( '"all" namespace disable checkboxes', function() {
+
+ // from Special:Recentchanges
+ var select =
+ '<select id="namespace" name="namespace" class="namespaceselector">'
+ + '<option value="" selected="selected">all</option>'
+ + '<option value="0">(Main)</option>'
+ + '<option value="1">Talk</option>'
+ + '<option value="2">User</option>'
+ + '<option value="3">User talk</option>'
+ + '<option value="4">ProjectName</option>'
+ + '<option value="5">ProjectName talk</option>'
+ + '</select>'
+ + '<input name="invert" type="checkbox" value="1" id="nsinvert" title="no title" />'
+ + '<label for="nsinvert" title="no title">Invert selection</label>'
+ + '<input name="associated" type="checkbox" value="1" id="nsassociated" title="no title" />'
+ + '<label for="nsassociated" title="no title">Associated namespace</label>'
+ + '<input type="submit" value="Go" />'
+ + '<input type="hidden" value="Special:RecentChanges" name="title" />'
+ ;
+
+ var $env = $( '<div>' ).html( select ).appendTo( 'body' );
+
+ // TODO abstract the double strictEquals
+
+ // At first checkboxes are enabled
+ strictEqual( $( '#nsinvert' ).attr( 'disabled' ), undefined );
+ strictEqual( $( '#nsassociated' ).attr( 'disabled' ), undefined );
+
+ // Initiate the recentchanges module
+ mw.special.recentchanges.init();
+
+ // By default
+ strictEqual( $( '#nsinvert' ).attr( 'disabled' ), 'disabled' );
+ strictEqual( $( '#nsassociated' ).attr( 'disabled' ), 'disabled' );
+
+ // select second option...
+ var $options = $( '#namespace' ).find( 'option' );
+ $options.eq(0).removeAttr( 'selected' );
+ $options.eq(1).attr( 'selected', 'selected' );
+ $( '#namespace' ).change();
+
+ // ... and checkboxes should be enabled again
+ strictEqual( $( '#nsinvert' ).attr( 'disabled' ), undefined );
+ strictEqual( $( '#nsassociated' ).attr( 'disabled' ), undefined );
+
+ // select first option ( 'all' namespace)...
+ $options.eq(1).removeAttr( 'selected' );
+ $options.eq(0).attr( 'selected', 'selected' );;
+ $( '#namespace' ).change();
+
+ // ... and checkboxes should now be disabled
+ strictEqual( $( '#nsinvert' ).attr( 'disabled' ), 'disabled' );
+ strictEqual( $( '#nsassociated' ).attr( 'disabled' ), 'disabled' );
+
+ // DOM cleanup
+ $env.remove();
+});
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.js b/tests/qunit/suites/resources/mediawiki/mediawiki.js
new file mode 100644
index 00000000..4beed881
--- /dev/null
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.js
@@ -0,0 +1,232 @@
+module( 'mediawiki.js' );
+
+test( '-- Initial check', function() {
+ expect(8);
+
+ ok( window.jQuery, 'jQuery defined' );
+ ok( window.$, '$j defined' );
+ ok( window.$j, '$j defined' );
+ strictEqual( window.$, window.jQuery, '$ alias to jQuery' );
+ strictEqual( window.$j, window.jQuery, '$j alias to jQuery' );
+
+ ok( window.mediaWiki, 'mediaWiki defined' );
+ ok( window.mw, 'mw defined' );
+ strictEqual( window.mw, window.mediaWiki, 'mw alias to mediaWiki' );
+});
+
+test( 'mw.Map', function() {
+ expect(17);
+
+ ok( mw.Map, 'mw.Map defined' );
+
+ var conf = new mw.Map(),
+ // Dummy variables
+ funky = function() {},
+ arry = [],
+ nummy = 7;
+
+ // Tests for input validation
+ strictEqual( conf.get( 'inexistantKey' ), null, 'Map.get returns null if selection was a string and the key was not found' );
+ strictEqual( conf.set( 'myKey', 'myValue' ), true, 'Map.set returns boolean true if a value was set for a valid key string' );
+ strictEqual( conf.set( funky, 'Funky' ), false, 'Map.set returns boolean false if key was invalid (Function)' );
+ strictEqual( conf.set( arry, 'Arry' ), false, 'Map.set returns boolean false if key was invalid (Array)' );
+ strictEqual( conf.set( nummy, 'Nummy' ), false, 'Map.set returns boolean false if key was invalid (Number)' );
+ equal( conf.get( 'myKey' ), 'myValue', 'Map.get returns a single value value correctly' );
+ strictEqual( conf.get( nummy ), null, 'Map.get ruturns null if selection was invalid (Number)' );
+ strictEqual( conf.get( funky ), null, 'Map.get ruturns null if selection was invalid (Function)' );
+
+ // Multiple values at once
+ var someValues = {
+ 'foo': 'bar',
+ 'lorem': 'ipsum',
+ 'MediaWiki': true
+ };
+ strictEqual( conf.set( someValues ), true, 'Map.set returns boolean true if multiple values were set by passing an object' );
+ deepEqual( conf.get( ['foo', 'lorem'] ), {
+ 'foo': 'bar',
+ 'lorem': 'ipsum'
+ }, 'Map.get returns multiple values correctly as an object' );
+
+ deepEqual( conf.get( ['foo', 'notExist'] ), {
+ 'foo': 'bar',
+ 'notExist': null
+ }, 'Map.get return includes keys that were not found as null values' );
+
+ strictEqual( conf.exists( 'foo' ), true, 'Map.exists returns boolean true if a key exists' );
+ strictEqual( conf.exists( 'notExist' ), false, 'Map.exists returns boolean false if a key does not exists' );
+
+ // Interacting with globals and accessing the values object
+ strictEqual( conf.get(), conf.values, 'Map.get returns the entire values object by reference (if called without arguments)' );
+
+ conf.set( 'globalMapChecker', 'Hi' );
+
+ ok( false === 'globalMapChecker' in window, 'new mw.Map did not store its values in the global window object by default' );
+
+ var globalConf = new mw.Map( true );
+ globalConf.set( 'anotherGlobalMapChecker', 'Hello' );
+
+ ok( 'anotherGlobalMapChecker' in window, 'new mw.Map( true ) did store its values in the global window object' );
+
+ // Whitelist this global variable for QUnit's 'noglobal' mode
+ if ( QUnit.config.noglobals ) {
+ QUnit.config.pollution.push( 'anotherGlobalMapChecker' );
+ }
+});
+
+test( 'mw.config', function() {
+ expect(1);
+
+ ok( mw.config instanceof mw.Map, 'mw.config instance of mw.Map' );
+});
+
+test( 'mw.message & mw.messages', function() {
+ expect(17);
+
+ ok( mw.messages, 'messages defined' );
+ ok( mw.messages instanceof mw.Map, 'mw.messages instance of mw.Map' );
+ ok( mw.messages.set( 'hello', 'Hello <b>awesome</b> world' ), 'mw.messages.set: Register' );
+
+ var hello = mw.message( 'hello' );
+
+ equal( hello.format, 'parse', 'Message property "format" defaults to "parse"' );
+ strictEqual( hello.map, mw.messages, 'Message property "map" defaults to the global instance in mw.messages' );
+ equal( hello.key, 'hello', 'Message property "key" (currect key)' );
+ deepEqual( hello.parameters, [], 'Message property "parameters" defaults to an empty array' );
+
+ // Todo
+ ok( hello.params, 'Message prototype "params"' );
+
+ hello.format = 'plain';
+ equal( hello.toString(), 'Hello <b>awesome</b> world', 'Message.toString returns the message as a string with the current "format"' );
+
+ equal( hello.escaped(), 'Hello &lt;b&gt;awesome&lt;/b&gt; world', 'Message.escaped returns the escaped message' );
+ equal( hello.format, 'escaped', 'Message.escaped correctly updated the "format" property' );
+
+ hello.parse();
+ equal( hello.format, 'parse', 'Message.parse correctly updated the "format" property' );
+
+ hello.plain();
+ equal( hello.format, 'plain', 'Message.plain correctly updated the "format" property' );
+
+ strictEqual( hello.exists(), true, 'Message.exists returns true for existing messages' );
+
+ var goodbye = mw.message( 'goodbye' );
+ strictEqual( goodbye.exists(), false, 'Message.exists returns false for inexisting messages' );
+
+ equal( goodbye.plain(), '<goodbye>', 'Message.toString returns plain <key> if format is "plain" and key does not exist' );
+ // bug 30684
+ equal( goodbye.escaped(), '&lt;goodbye&gt;', 'Message.toString returns properly escaped &lt;key&gt; if format is "escaped" and key does not exist' );
+});
+
+test( 'mw.msg', function() {
+ expect(3);
+
+ ok( mw.messages.set( 'hello', 'Hello <b>awesome</b> world' ), 'mw.messages.set: Register' );
+
+ equal( mw.msg( 'hello' ), 'Hello <b>awesome</b> world', 'Gets message with default options (existing message)' );
+ equal( mw.msg( 'goodbye' ), '<goodbye>', 'Gets message with default options (inexisting message)' );
+});
+
+test( 'mw.loader', function() {
+ expect(5);
+
+ // Regular expression to extract the path for the QUnit tests
+ // Takes into account that tests could be run from a file:// URL
+ // by excluding the 'index.html' part from the URL
+ var rePath = /(?:[^#\?](?!index.html))*\/?/;
+
+ // Four assertions to test the above regular expression:
+ equal(
+ rePath.exec( 'http://path/to/tests/?foobar' )[0],
+ "http://path/to/tests/",
+ "Extracting path from http URL with query"
+ );
+ equal(
+ rePath.exec( 'http://path/to/tests/#frag' )[0],
+ "http://path/to/tests/",
+ "Extracting path from http URL with fragment"
+ );
+ equal(
+ rePath.exec( 'file://path/to/tests/index.html?foobar' )[0],
+ "file://path/to/tests/",
+ "Extracting path from local URL (file://) with query"
+ );
+ equal(
+ rePath.exec( 'file://path/to/tests/index.html#frag' )[0],
+ "file://path/to/tests/",
+ "Extracting path from local URL (file://) with fragment"
+ );
+
+ // Asynchronous ahead
+ stop(5000);
+
+ // Extract path
+ var tests_path = rePath.exec( location.href );
+
+ mw.loader.implement( 'is.awesome', [QUnit.fixurl( tests_path + 'data/defineTestCallback.js')], {}, {} );
+
+ mw.loader.using( 'is.awesome', function() {
+
+ // /sample/awesome.js declares the "mw.loader.testCallback" function
+ // which contains a call to start() and ok()
+ mw.loader.testCallback();
+ mw.loader.testCallback = undefined;
+
+ }, function() {
+ start();
+ ok( false, 'Error callback fired while implementing "is.awesome" module' );
+ });
+
+});
+
+test( 'mw.loader.bug29107' , function() {
+ expect(2);
+
+ // Message doesn't exist already
+ ok( !mw.messages.exists( 'bug29107' ) );
+
+ // Async! Include a timeout, as failure in this test leads to neither the
+ // success nor failure callbacks getting called.
+ stop(5000);
+
+ mw.loader.implement( 'bug29107.messages-only', [], {}, {'bug29107': 'loaded'} );
+ mw.loader.using( 'bug29107.messages-only', function() {
+ start();
+ ok( mw.messages.exists( 'bug29107' ), 'Bug 29107: messages-only module should implement ok' );
+ }, function() {
+ start();
+ ok( false, 'Error callback fired while implementing "bug29107.messages-only" module' );
+ });
+});
+
+test( 'mw.html', function() {
+ expect(7);
+
+ raises( function(){
+ mw.html.escape();
+ }, TypeError, 'html.escape throws a TypeError if argument given is not a string' );
+
+ equal( mw.html.escape( '<mw awesome="awesome" value=\'test\' />' ),
+ '&lt;mw awesome=&quot;awesome&quot; value=&#039;test&#039; /&gt;', 'html.escape escapes html snippet' );
+
+ equal( mw.html.element(),
+ '<undefined/>', 'html.element Always return a valid html string (even without arguments)' );
+
+ equal( mw.html.element( 'div' ), '<div/>', 'html.element DIV (simple)' );
+
+ equal( mw.html.element( 'div',
+ { id: 'foobar' } ),
+ '<div id="foobar"/>',
+ 'html.element DIV (attribs)' );
+
+ equal( mw.html.element( 'div',
+ null, 'a' ),
+ '<div>a</div>',
+ 'html.element DIV (content)' );
+
+ equal( mw.html.element( 'a',
+ { href: 'http://mediawiki.org/w/index.php?title=RL&action=history' }, 'a' ),
+ '<a href="http://mediawiki.org/w/index.php?title=RL&amp;action=history">a</a>',
+ 'html.element DIV (attribs + content)' );
+
+});
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js
new file mode 100644
index 00000000..52cd32c8
--- /dev/null
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js
@@ -0,0 +1,35 @@
+/* Some misc JavaScript compatibility tests, just to make sure the environments we run in are consistent */
+
+module( 'mediawiki.jscompat' );
+
+test( 'Variable with Unicode letter in name', function() {
+ expect(3);
+ var orig = "some token";
+ var Åablono = orig;
+ deepEqual( Åablono, orig, 'Åablono' );
+ deepEqual( \u015dablono, orig, '\\u015dablono' );
+ deepEqual( \u015Dablono, orig, '\\u015Dablono' );
+});
+
+/*
+// Not that we need this. ;)
+// This fails on IE 6-8
+// Works on IE 9, Firefox 6, Chrome 14
+test( 'Keyword workaround: "if" as variable name using Unicode escapes', function() {
+ var orig = "another token";
+ \u0069\u0066 = orig;
+ deepEqual( \u0069\u0066, orig, '\\u0069\\u0066' );
+});
+*/
+
+/*
+// Not that we need this. ;)
+// This fails on IE 6-9
+// Works on Firefox 6, Chrome 14
+test( 'Keyword workaround: "if" as member variable name using Unicode escapes', function() {
+ var orig = "another token";
+ var foo = {};
+ foo.\u0069\u0066 = orig;
+ deepEqual( foo.\u0069\u0066, orig, 'foo.\\u0069\\u0066' );
+});
+*/
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.user.js b/tests/qunit/suites/resources/mediawiki/mediawiki.user.js
new file mode 100644
index 00000000..d5c6baad
--- /dev/null
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.user.js
@@ -0,0 +1,29 @@
+module( 'mediawiki.user.js' );
+
+test( '-- Initial check', function() {
+ expect(1);
+
+ ok( mw.user, 'mw.user defined' );
+});
+
+
+test( 'options', function() {
+ expect(1);
+
+ ok( mw.user.options instanceof mw.Map, 'options instance of mw.Map' );
+});
+
+test( 'User login status', function() {
+ expect(5);
+
+ strictEqual( mw.user.name(), null, 'user.name should return null when anonymous' );
+ ok( mw.user.anonymous(), 'user.anonymous should reutrn true when anonymous' );
+
+ // Not part of startUp module
+ mw.config.set( 'wgUserName', 'John' );
+
+ equal( mw.user.name(), 'John', 'user.name returns username when logged-in' );
+ ok( !mw.user.anonymous(), 'user.anonymous returns false when logged-in' );
+
+ equal( mw.user.id(), 'John', 'user.id Returns username when logged-in' );
+});
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.util.js b/tests/qunit/suites/resources/mediawiki/mediawiki.util.js
new file mode 100644
index 00000000..9c05d9b2
--- /dev/null
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.util.js
@@ -0,0 +1,307 @@
+module( 'mediawiki.util.js' );
+
+test( '-- Initial check', function() {
+ expect(1);
+
+ ok( mw.util, 'mw.util defined' );
+});
+
+test( 'rawurlencode', function() {
+ expect(1);
+
+ equal( mw.util.rawurlencode( 'Test:A & B/Here' ), 'Test%3AA%20%26%20B%2FHere' );
+});
+
+test( 'wikiUrlencode', function() {
+ expect(1);
+
+ equal( mw.util.wikiUrlencode( 'Test:A & B/Here' ), 'Test:A_%26_B/Here' );
+});
+
+test( 'wikiGetlink', function() {
+ expect(3);
+
+ // Not part of startUp module
+ mw.config.set( 'wgArticlePath', '/wiki/$1' );
+ mw.config.set( 'wgPageName', 'Foobar' );
+
+ var hrefA = mw.util.wikiGetlink( 'Sandbox' );
+ equal( hrefA, '/wiki/Sandbox', 'Simple title; Get link for "Sandbox"' );
+
+ var hrefB = mw.util.wikiGetlink( 'Foo:Sandbox ? 5+5=10 ! (test)/subpage' );
+ equal( hrefB, '/wiki/Foo:Sandbox_%3F_5%2B5%3D10_%21_%28test%29/subpage',
+ 'Advanced title; Get link for "Foo:Sandbox ? 5+5=10 ! (test)/subpage"' );
+
+ var hrefC = mw.util.wikiGetlink();
+ equal( hrefC, '/wiki/Foobar', 'Default title; Get link for current page ("Foobar")' );
+});
+
+test( 'wikiScript', function() {
+ expect(2);
+
+ mw.config.set({
+ 'wgScript': '/w/index.php',
+ 'wgScriptPath': '/w',
+ 'wgScriptExtension': '.php'
+ });
+
+ equal( mw.util.wikiScript(), mw.config.get( 'wgScript' ), 'Defaults to index.php and is equal to wgScript' );
+ equal( mw.util.wikiScript( 'api' ), '/w/api.php', 'API path' );
+
+});
+
+test( 'addCSS', function() {
+ expect(3);
+
+ var $testEl = $( '<div>' ).attr( 'id', 'mw-addcsstest' ).appendTo( 'body' );
+
+ var style = mw.util.addCSS( '#mw-addcsstest { visibility: hidden; }' );
+ equal( typeof style, 'object', 'addCSS returned an object' );
+ strictEqual( style.disabled, false, 'property "disabled" is available and set to false' );
+
+ equal( $testEl.css( 'visibility' ), 'hidden', 'Added style properties are in effect' );
+
+ // Clean up
+ $( style.ownerNode )
+ .add( $testEl )
+ .remove();
+});
+
+test( 'toggleToc', function() {
+ expect(4);
+
+ strictEqual( mw.util.toggleToc(), null, 'Return null if there is no table of contents on the page.' );
+
+ var tocHtml =
+ '<table id="toc" class="toc"><tr><td>' +
+ '<div id="toctitle">' +
+ '<h2>Contents</h2>' +
+ '<span class="toctoggle">&nbsp;[<a href="#" class="internal" id="togglelink">Hide</a>&nbsp;]</span>' +
+ '</div>' +
+ '<ul><li></li></ul>' +
+ '</td></tr></table>',
+ $toc = $(tocHtml).appendTo( 'body' ),
+ $toggleLink = $( '#togglelink' );
+
+ strictEqual( $toggleLink.length, 1, 'Toggle link is appended to the page.' );
+
+ // Toggle animation is asynchronous
+ // QUnit should not finish this test() untill they are all done
+ stop();
+
+ var actionC = function() {
+ start();
+
+ // Clean up
+ $toc.remove();
+ };
+ var actionB = function() {
+ start(); stop();
+ strictEqual( mw.util.toggleToc( $toggleLink, actionC ), true, 'Return boolean true if the TOC is now visible.' );
+ };
+ var actionA = function() {
+ strictEqual( mw.util.toggleToc( $toggleLink, actionB ), false, 'Return boolean false if the TOC is now hidden.' );
+ };
+
+ actionA();
+});
+
+test( 'getParamValue', function() {
+ expect(5);
+
+ var url1 = 'http://mediawiki.org/?foo=wrong&foo=right#&foo=bad';
+
+ equal( mw.util.getParamValue( 'foo', url1 ), 'right', 'Use latest one, ignore hash' );
+ strictEqual( mw.util.getParamValue( 'bar', url1 ), null, 'Return null when not found' );
+
+ var url2 = 'http://mediawiki.org/#&foo=bad';
+ strictEqual( mw.util.getParamValue( 'foo', url2 ), null, 'Ignore hash if param is not in querystring but in hash (bug 27427)' );
+
+ var url3 = 'example.com?' + $.param({ 'TEST': 'a b+c' });
+ strictEqual( mw.util.getParamValue( 'TEST', url3 ), 'a b+c', 'Bug 30441: getParamValue must understand "+" encoding of space' );
+
+ var url4 = 'example.com?' + $.param({ 'TEST': 'a b+c d' }); // check for sloppy code from r95332 :)
+ strictEqual( mw.util.getParamValue( 'TEST', url4 ), 'a b+c d', 'Bug 30441: getParamValue must understand "+" encoding of space (multiple spaces)' );
+});
+
+test( 'tooltipAccessKey', function() {
+ expect(3);
+
+ equal( typeof mw.util.tooltipAccessKeyPrefix, 'string', 'mw.util.tooltipAccessKeyPrefix must be a string' );
+ ok( mw.util.tooltipAccessKeyRegexp instanceof RegExp, 'mw.util.tooltipAccessKeyRegexp instance of RegExp' );
+ ok( mw.util.updateTooltipAccessKeys, 'mw.util.updateTooltipAccessKeys' );
+});
+
+test( '$content', function() {
+ expect(2);
+
+ ok( mw.util.$content instanceof jQuery, 'mw.util.$content instance of jQuery' );
+ strictEqual( mw.util.$content.length, 1, 'mw.util.$content must have length of 1' );
+});
+
+test( 'addPortletLink', function() {
+ expect(7);
+
+ var mwPanel = '<div id="mw-panel" class="noprint">\
+ <h5>Toolbox</h5>\
+ <div class="portlet" id="p-tb">\
+ <ul class="body"></ul>\
+ </div>\
+</div>',
+ vectorTabs = '<div id="p-views" class="vectorTabs">\
+ <h5>Views</h5>\
+ <ul></ul>\
+</div>',
+ $mwPanel = $(mwPanel).appendTo( 'body' ),
+ $vectorTabs = $(vectorTabs).appendTo( 'body' );
+
+ var tbRL = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/wiki/ResourceLoader',
+ 'ResourceLoader', 't-rl', 'More info about ResourceLoader on MediaWiki.org ', 'l' );
+
+ ok( $.isDomElement( tbRL ), 'addPortletLink returns a valid DOM Element according to $.isDomElement' );
+
+ var tbMW = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/',
+ 'MediaWiki.org', 't-mworg', 'Go to MediaWiki.org ', 'm', tbRL ),
+ $tbMW = $( tbMW );
+
+
+ equal( $tbMW.attr( 'id' ), 't-mworg', 'Link has correct ID set' );
+ equal( $tbMW.closest( '.portlet' ).attr( 'id' ), 'p-tb', 'Link was inserted within correct portlet' );
+ equal( $tbMW.next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing nextnode)' );
+
+ var tbRLDM = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/wiki/RL/DM',
+ 'Default modules', 't-rldm', 'List of all default modules ', 'd', '#t-rl' );
+
+ equal( $( tbRLDM ).next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing CSS selector)' );
+
+ var caFoo = mw.util.addPortletLink( 'p-views', '#', 'Foo' );
+
+ strictEqual( $tbMW.find( 'span').length, 0, 'No <span> element should be added for porlets without vectorTabs class.' );
+ strictEqual( $( caFoo ).find( 'span').length, 1, 'A <span> element should be added for porlets with vectorTabs class.' );
+
+ // Clean up
+ $( [tbRL, tbMW, tbRLDM, caFoo] )
+ .add( $mwPanel )
+ .add( $vectorTabs )
+ .remove();
+});
+
+test( 'jsMessage', function() {
+ expect(1);
+
+ var a = mw.util.jsMessage( "MediaWiki is <b>Awesome</b>." );
+ ok( a, 'Basic checking of return value' );
+
+ // Clean up
+ $( '#mw-js-message' ).remove();
+});
+
+test( 'validateEmail', function() {
+ expect(6);
+
+ strictEqual( mw.util.validateEmail( "" ), null, 'Should return null for empty string ' );
+ strictEqual( mw.util.validateEmail( "user@localhost" ), true, 'Return true for a valid e-mail address' );
+
+ // testEmailWithCommasAreInvalids
+ strictEqual( mw.util.validateEmail( "user,foo@example.org" ), false, 'Emails with commas are invalid' );
+ strictEqual( mw.util.validateEmail( "userfoo@ex,ample.org" ), false, 'Emails with commas are invalid' );
+
+ // testEmailWithHyphens
+ strictEqual( mw.util.validateEmail( "user-foo@example.org" ), true, 'Emails may contain a hyphen' );
+ strictEqual( mw.util.validateEmail( "userfoo@ex-ample.org" ), true, 'Emails may contain a hyphen' );
+});
+
+test( 'isIPv6Address', function() {
+ expect(40);
+
+ // Shortcuts
+ var assertFalseIPv6 = function( addy, summary ) {
+ return strictEqual( mw.util.isIPv6Address( addy ), false, summary );
+ },
+ assertTrueIPv6 = function( addy, summary ) {
+ return strictEqual( mw.util.isIPv6Address( addy ), true, summary );
+ };
+
+ // Based on IPTest.php > testisIPv6
+ assertFalseIPv6( ':fc:100::', 'IPv6 starting with lone ":"' );
+ assertFalseIPv6( 'fc:100:::', 'IPv6 ending with a ":::"' );
+ assertFalseIPv6( 'fc:300', 'IPv6 with only 2 words' );
+ assertFalseIPv6( 'fc:100:300', 'IPv6 with only 3 words' );
+
+ $.each(
+ ['fc:100::',
+ 'fc:100:a::',
+ 'fc:100:a:d::',
+ 'fc:100:a:d:1::',
+ 'fc:100:a:d:1:e::',
+ 'fc:100:a:d:1:e:ac::'], function( i, addy ){
+ assertTrueIPv6( addy, addy + ' is a valid IP' );
+ });
+
+ assertFalseIPv6( 'fc:100:a:d:1:e:ac:0::', 'IPv6 with 8 words ending with "::"' );
+ assertFalseIPv6( 'fc:100:a:d:1:e:ac:0:1::', 'IPv6 with 9 words ending with "::"' );
+
+ assertFalseIPv6( ':::' );
+ assertFalseIPv6( '::0:', 'IPv6 ending in a lone ":"' );
+
+ assertTrueIPv6( '::', 'IPv6 zero address' );
+ $.each(
+ ['::0',
+ '::fc',
+ '::fc:100',
+ '::fc:100:a',
+ '::fc:100:a:d',
+ '::fc:100:a:d:1',
+ '::fc:100:a:d:1:e',
+ '::fc:100:a:d:1:e:ac',
+
+ 'fc:100:a:d:1:e:ac:0'], function( i, addy ){
+ assertTrueIPv6( addy, addy + ' is a valid IP' );
+ });
+
+ assertFalseIPv6( '::fc:100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
+ assertFalseIPv6( '::fc:100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
+
+ assertFalseIPv6( ':fc::100', 'IPv6 starting with lone ":"' );
+ assertFalseIPv6( 'fc::100:', 'IPv6 ending with lone ":"' );
+ assertFalseIPv6( 'fc:::100', 'IPv6 with ":::" in the middle' );
+
+ assertTrueIPv6( 'fc::100', 'IPv6 with "::" and 2 words' );
+ assertTrueIPv6( 'fc::100:a', 'IPv6 with "::" and 3 words' );
+ assertTrueIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' );
+ assertTrueIPv6( 'fc::100:a:d:1', 'IPv6 with "::" and 5 words' );
+ assertTrueIPv6( 'fc::100:a:d:1:e', 'IPv6 with "::" and 6 words' );
+ assertTrueIPv6( 'fc::100:a:d:1:e:ac', 'IPv6 with "::" and 7 words' );
+ assertTrueIPv6( '2001::df', 'IPv6 with "::" and 2 words' );
+ assertTrueIPv6( '2001:5c0:1400:a::df', 'IPv6 with "::" and 5 words' );
+ assertTrueIPv6( '2001:5c0:1400:a::df:2', 'IPv6 with "::" and 6 words' );
+
+ assertFalseIPv6( 'fc::100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
+ assertFalseIPv6( 'fc::100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
+});
+
+test( 'isIPv4Address', function() {
+ expect(11);
+
+ // Shortcuts
+ var assertFalseIPv4 = function( addy, summary ) {
+ return strictEqual( mw.util.isIPv4Address( addy ), false, summary );
+ },
+ assertTrueIPv4 = function( addy, summary ) {
+ return strictEqual( mw.util.isIPv4Address( addy ), true, summary );
+ };
+
+ // Based on IPTest.php > testisIPv4
+ assertFalseIPv4( false, 'Boolean false is not an IP' );
+ assertFalseIPv4( true, 'Boolean true is not an IP' );
+ assertFalseIPv4( '', 'Empty string is not an IP' );
+ assertFalseIPv4( 'abc', '"abc" is not an IP' );
+ assertFalseIPv4( ':', 'Colon is not an IP' );
+ assertFalseIPv4( '124.24.52', 'IPv4 not enough quads' );
+ assertFalseIPv4( '24.324.52.13', 'IPv4 out of range' );
+ assertFalseIPv4( '.24.52.13', 'IPv4 starts with period' );
+
+ assertTrueIPv4( '124.24.52.13', '124.24.52.134 is a valid IP' );
+ assertTrueIPv4( '1.24.52.13', '1.24.52.13 is a valid IP' );
+ assertFalseIPv4( '74.24.52.13/20', 'IPv4 ranges are not recogzized as valid IPs' );
+});
diff --git a/tests/selenium/Selenium.php b/tests/selenium/Selenium.php
new file mode 100644
index 00000000..ecf7f9ec
--- /dev/null
+++ b/tests/selenium/Selenium.php
@@ -0,0 +1,190 @@
+<?php
+/**
+ * Selenium connector
+ * This is implemented as a singleton.
+ */
+
+require( 'Testing/Selenium.php' );
+
+class Selenium {
+ protected static $_instance = null;
+
+ public $isStarted = false;
+ public $tester;
+
+ protected $port;
+ protected $host;
+ protected $browser;
+ protected $browsers;
+ protected $logger;
+ protected $user;
+ protected $pass;
+ protected $timeout = 30000;
+ protected $verbose;
+ protected $junitlogfile; //processed by phpUnderControl
+ protected $runagainstgrid = false;
+
+ /**
+ * @todo this shouldn't have to be static
+ */
+ static protected $url;
+
+ /**
+ * Override parent
+ */
+ public function __construct() {
+ /**
+ * @todo this is an ugly hack to make information available to
+ * other tests. It should be fixed.
+ */
+ if ( null === self::$_instance ) {
+ self::$_instance = $this;
+ } else {
+ throw new MWException( "Already have one Selenium instance." );
+ }
+ }
+
+ public function start() {
+ $this->tester = new Testing_Selenium( $this->browser, self::$url, $this->host,
+ $this->port, $this->timeout );
+ if ( method_exists( $this->tester, "setVerbose" ) ) $this->tester->setVerbose( $this->verbose );
+
+ $this->tester->start();
+ $this->isStarted = true;
+ }
+
+ public function stop() {
+ $this->tester->stop();
+ $this->tester = null;
+ $this->isStarted = false;
+ }
+
+ public function login() {
+ if ( strlen( $this->user ) == 0 ) {
+ return;
+ }
+ $this->open( self::$url . '/index.php?title=Special:Userlogin' );
+ $this->type( 'wpName1', $this->user );
+ $this->type( 'wpPassword1', $this->pass );
+ $this->click( "//input[@id='wpLoginAttempt']" );
+ $this->waitForPageToLoad( 10000 );
+
+ // after login we redirect to the main page. So check whether the "Prefernces" top menu item exists
+ $value = $this->isElementPresent( "//li[@id='pt-preferences']" );
+
+ if ( $value != true ) {
+ throw new Testing_Selenium_Exception( "Login Failed" );
+ }
+
+ }
+
+ public static function getInstance() {
+ if ( null === self::$_instance ) {
+ throw new MWException( "No instance set yet" );
+ }
+
+ return self::$_instance;
+ }
+
+ public function loadPage( $title, $action ) {
+ $this->open( self::$url . '/index.php?title=' . $title . '&action=' . $action );
+ }
+
+ public function setLogger( $logger ) {
+ $this->logger = $logger;
+ }
+
+ public function getLogger( ) {
+ return $this->logger;
+ }
+
+ public function log( $message ) {
+ $this->logger->write( $message );
+ }
+
+ public function setUrl( $url ) {
+ self::$url = $url;
+ }
+
+ static public function getUrl() {
+ return self::$url;
+ }
+
+ public function setPort( $port ) {
+ $this->port = $port;
+ }
+
+ public function getPort() {
+ return $this->port;
+ }
+
+ public function setUser( $user ) {
+ $this->user = $user;
+ }
+
+ // Function to get username
+ public function getUser() {
+ return $this->user;
+ }
+
+
+ public function setPass( $pass ) {
+ $this->pass = $pass;
+ }
+
+ //add function to get password
+ public function getPass( ) {
+ return $this->pass;
+ }
+
+
+ public function setHost( $host ) {
+ $this->host = $host;
+ }
+
+ public function setVerbose( $verbose ) {
+ $this->verbose = $verbose;
+ }
+
+ public function setAvailableBrowsers( $availableBrowsers ) {
+ $this->browsers = $availableBrowsers;
+ }
+
+ public function setJUnitLogfile( $junitlogfile ) {
+ $this->junitlogfile = $junitlogfile;
+ }
+
+ public function getJUnitLogfile( ) {
+ return $this->junitlogfile;
+ }
+
+ public function setRunAgainstGrid( $runagainstgrid ) {
+ $this->runagainstgrid = $runagainstgrid;
+ }
+
+ public function setBrowser( $b ) {
+ if ($this->runagainstgrid) {
+ $this->browser = $b;
+ return true;
+ }
+ if ( !isset( $this->browsers[$b] ) ) {
+ throw new MWException( "Invalid Browser: $b.\n" );
+ }
+
+ $this->browser = $this->browsers[$b];
+ }
+
+ public function getAvailableBrowsers() {
+ return $this->browsers;
+ }
+
+ public function __call( $name, $args ) {
+ $t = call_user_func_array( array( $this->tester, $name ), $args );
+ return $t;
+ }
+
+ // Prevent external cloning
+ protected function __clone() { }
+ // Prevent external construction
+ // protected function __construct() {}
+}
diff --git a/tests/selenium/SeleniumConfig.php b/tests/selenium/SeleniumConfig.php
new file mode 100644
index 00000000..b8cdf1c5
--- /dev/null
+++ b/tests/selenium/SeleniumConfig.php
@@ -0,0 +1,113 @@
+<?php
+if ( !defined( 'SELENIUMTEST' ) ) {
+ die( 1 );
+}
+
+class SeleniumConfig {
+
+ /*
+ * Retreives the Selenium configuration values from an ini file.
+ * See sample config file in selenium_settings.ini.sample
+ *
+ */
+
+ public static function getSeleniumSettings ( &$seleniumSettings,
+ &$seleniumBrowsers,
+ &$seleniumTestSuites,
+ $seleniumConfigFile = null ) {
+ if ( strlen( $seleniumConfigFile ) == 0 ) {
+ global $wgSeleniumConfigFile;
+ if ( isset( $wgSeleniumConfigFile ) ) $seleniumConfigFile = $wgSeleniumConfigFile ;
+ }
+
+ if ( strlen( $seleniumConfigFile ) == 0 || !file_exists( $seleniumConfigFile ) ) {
+ throw new MWException( "Unable to read local Selenium Settings from " . $seleniumConfigFile . "\n" );
+ }
+
+ if ( !defined( 'PHP_VERSION_ID' ) ||
+ ( PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3 ) ) {
+ $configArray = self::parse_5_2_ini_file( $seleniumConfigFile );
+ } else {
+ $configArray = parse_ini_file( $seleniumConfigFile, true );
+ }
+ if ( $configArray === false ) {
+ throw new MWException( "Error parsing " . $seleniumConfigFile . "\n" );
+ }
+
+ if ( array_key_exists( 'SeleniumSettings', $configArray) ) {
+ wfSuppressWarnings();
+ //we may need to change how this is set. But for now leave it in the ini file
+ $seleniumBrowsers = $configArray['SeleniumSettings']['browsers'];
+
+ $seleniumSettings['host'] = $configArray['SeleniumSettings']['host'];
+ $seleniumSettings['port'] = $configArray['SeleniumSettings']['port'];
+ $seleniumSettings['wikiUrl'] = $configArray['SeleniumSettings']['wikiUrl'];
+ $seleniumSettings['username'] = $configArray['SeleniumSettings']['username'];
+ $seleniumSettings['userPassword'] = $configArray['SeleniumSettings']['userPassword'];
+ $seleniumSettings['testBrowser'] = $configArray['SeleniumSettings']['testBrowser'];
+ $seleniumSettings['startserver'] = $configArray['SeleniumSettings']['startserver'];
+ $seleniumSettings['stopserver'] = $configArray['SeleniumSettings']['stopserver'];
+ $seleniumSettings['seleniumserverexecpath'] = $configArray['SeleniumSettings']['seleniumserverexecpath'];
+ $seleniumSettings['jUnitLogFile'] = $configArray['SeleniumSettings']['jUnitLogFile'];
+ $seleniumSettings['runAgainstGrid'] = $configArray['SeleniumSettings']['runAgainstGrid'];
+
+ wfRestoreWarnings();
+ }
+ if ( array_key_exists( 'SeleniumTests', $configArray) ) {
+ wfSuppressWarnings();
+ $seleniumTestSuites = $configArray['SeleniumTests']['testSuite'];
+ wfRestoreWarnings();
+ }
+ return true;
+ }
+
+ /**
+ * PHP 5.2 parse_ini_file() doesn't have support for array keys.
+ * This function parses simple ini files with such syntax using just
+ * 5.2 functions.
+ */
+ private static function parse_5_2_ini_file( $ConfigFile ) {
+ $file = fopen( $ConfigFile, "rt" );
+ if ( !$file ) {
+ return false;
+ }
+ $header = '';
+
+ $configArray = array();
+
+ while ( ( $line = fgets( $file ) ) !== false ) {
+ $line = strtok( $line, "\r\n" );
+
+ if ( !$line || $line[0] == ';' ) continue;
+
+ if ( $line[0] == '[' && substr( $line, -1 ) == ']' ) {
+ $header = substr( $line, 1, -1 );
+ $configArray[$header] = array();
+ } else {
+ $configArray[$header] = array_merge_recursive( $configArray[$header], self::parse_ini_line( $line ) );
+ }
+ }
+ return $configArray;
+ }
+
+ private static function parse_ini_line( $iniLine ) {
+ static $specialValues = array( 'false' => false, 'true' => true, 'null' => null );
+ list( $key, $value ) = explode( '=', $iniLine, 2 );
+ $key = trim( $key );
+ $value = trim( $value );
+
+ if ( isset( $specialValues[$value] ) ) {
+ $value = $specialValues[$value];
+ } else {
+ $value = trim( $value, '"' );
+ }
+
+ /* Support one-level arrays */
+ if ( preg_match( '/^([A-Za-z]+)\[([A-Za-z]+)\]/', $key, $m ) ) {
+ $key = $m[1];
+ $value = array( $m[2] => $value );
+ }
+
+ return array( $key => $value );
+ }
+}
diff --git a/tests/selenium/SeleniumLoader.php b/tests/selenium/SeleniumLoader.php
new file mode 100644
index 00000000..8d5e7713
--- /dev/null
+++ b/tests/selenium/SeleniumLoader.php
@@ -0,0 +1,9 @@
+<?php
+
+class SeleniumLoader {
+ static function load() {
+ require_once( 'Testing/Selenium.php' );
+ require_once( 'PHPUnit/Framework.php' );
+ require_once( 'PHPUnit/Extensions/SeleniumTestCase.php' );
+ }
+}
diff --git a/tests/selenium/SeleniumServerManager.php b/tests/selenium/SeleniumServerManager.php
new file mode 100644
index 00000000..9e2d4aa8
--- /dev/null
+++ b/tests/selenium/SeleniumServerManager.php
@@ -0,0 +1,239 @@
+<?php
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com>
+ * http://citizendium.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ */
+
+class SeleniumServerManager {
+ private $SeleniumStartServer = false;
+ private $OS = '';
+ private $SeleniumServerPid = 'NaN';
+ private $SeleniumServerPort = 4444;
+ private $SeleniumServerStartTimeout = 10; // 10 secs.
+ private $SeleniumServerExecPath;
+
+ public function __construct( $startServer,
+ $serverPort,
+ $serverExecPath ) {
+ $this->OS = (string) PHP_OS;
+ if ( isset( $startServer ) )
+ $this->SeleniumStartServer = $startServer;
+ if ( isset( $serverPort ) )
+ $this->SeleniumServerPort = $serverPort;
+ if ( isset( $serverExecPath ) )
+ $this->SeleniumServerExecPath = $serverExecPath;
+ return;
+ }
+
+ // Getters for certain private attributes. No setters, since they
+ // should not change after the manager object is created.
+
+ public function getSeleniumStartServer() {
+ return $this->SeleniumStartServer;
+ }
+
+ public function getSeleniumServerPort() {
+ return $this->SeleniumServerPort;
+ }
+
+ public function getSeleniumServerPid() {
+ return $this->SeleniumServerPid;
+ }
+
+ // Changing value of SeleniumStartServer allows starting server after
+ // creation of the class instance. Only allow setting SeleniumStartServer
+ // to true, since after server is started, it is shut down by stop().
+
+ public function setSeleniumStartServer( $startServer ) {
+ if ( $startServer == true ) $this->SeleniumStartServer = true;
+ }
+
+ // return values are: 1) started - server started, 2) failed -
+ // server not started, 3) running - instructed to start server, but
+ // server already running
+
+ public function start() {
+
+ if ( !$this->SeleniumStartServer ) return 'failed';
+
+ // commented out cases are untested
+
+ switch ( $this->OS ) {
+ case "Linux":
+# case' CYGWIN_NT-5.1':
+ case 'Darwin':
+# case 'FreeBSD':
+# case 'HP-UX':
+# case 'IRIX64':
+# case 'NetBSD':
+# case 'OpenBSD':
+# case 'SunOS':
+# case 'Unix':
+ // *nix based OS
+ return $this->startServerOnUnix();
+ break;
+ case "Windows":
+ case "WIN32":
+ case "WINNT":
+ // Windows
+ return $this->startServerOnWindows();
+ break;
+ default:
+ // An untested OS
+ return 'failed';
+ break;
+ }
+ }
+
+ public function stop() {
+
+ // commented out cases are untested
+
+ switch ( $this->OS ) {
+ case "Linux":
+# case' CYGWIN_NT-5.1':
+ case 'Darwin':
+# case 'FreeBSD':
+# case 'HP-UX':
+# case 'IRIX64':
+# case 'NetBSD':
+# case 'OpenBSD':
+# case 'SunOS':
+# case 'Unix':
+ // *nix based OS
+ return $this->stopServerOnUnix();
+ break;
+ case "Windows":
+ case "WIN32":
+ case "WINNT":
+ // Windows
+ return $this->stopServerOnWindows();
+ break;
+ default:
+ // An untested OS
+ return 'failed';
+ break;
+ }
+ }
+
+ private function startServerOnUnix() {
+
+ $output = array();
+ $user = $_ENV['USER'];
+ // @todo FIXME: This should be a little more generalized :)
+ if (PHP_OS == 'Darwin') {
+ // Mac OS X's ps barfs on the 'w' param, but doesn't need it.
+ $ps = "ps -U %s";
+ } else {
+ // Good on Linux
+ $ps = "ps -U %s w";
+ }
+ $psCommand = sprintf($ps, escapeshellarg($user));
+ exec($psCommand . " | grep -i selenium-server", $output);
+
+ // Start server. If there is already a server running,
+ // return running.
+
+ if ( isset( $this->SeleniumServerExecPath ) ) {
+ $found = 0;
+ foreach ( $output as $string ) {
+ $found += preg_match(
+ '~^(.*)java(.+)-jar(.+)selenium-server~',
+ $string );
+ }
+ if ( $found == 0 ) {
+
+ // Didn't find the selenium server. Start it up.
+ // First set up comamand line suffix.
+ // NB: $! is pid of last job run in background
+ // The echo guarentees it is put into $op when
+ // the exec command is run.
+
+ $commandSuffix = ' > /dev/null 2>&1'. ' & echo $!';
+ $portText = ' -port ' . $this->SeleniumServerPort;
+ $command = "java -jar " .
+ escapeshellarg($this->SeleniumServerExecPath) .
+ $portText . $commandSuffix;
+ exec($command ,$op);
+ $pid = (int)$op[0];
+ if ( $pid != "" )
+ $this->SeleniumServerPid = $pid;
+ else {
+ $this->SeleniumServerPid = 'NaN';
+ // Server start failed.
+ return 'failed';
+ }
+ // Wait for the server to startup and listen
+ // on its port. Note: this solution kinda
+ // stinks, since it uses a wait loop - dnessett
+
+ wfSuppressWarnings();
+ for ( $cnt = 1;
+ $cnt <= $this->SeleniumServerStartTimeout;
+ $cnt++ ) {
+ $fp = fsockopen ( 'localhost',
+ $this->SeleniumServerPort,
+ $errno, $errstr, 0 );
+ if ( !$fp ) {
+ sleep( 1 );
+ continue;
+ // Server start succeeded.
+ } else {
+ fclose ( $fp );
+ return 'started';
+ }
+ }
+ wfRestoreWarnings();
+ echo ( "Starting Selenium server timed out.\n" );
+ return 'failed';
+ }
+ // server already running.
+ else return 'running';
+
+ }
+ // No Server execution path defined.
+ return 'failed';
+ }
+
+ private function startServerOnWindows() {
+ // Unimplemented.
+ return 'failed';
+ }
+
+ private function stopServerOnUnix() {
+
+ if ( !empty( $this->SeleniumServerPid ) &&
+ $this->SeleniumServerPid != 'NaN' ) {
+ exec( "kill -9 " . $this->SeleniumServerPid );
+ return 'stopped';
+ }
+ else return 'failed';
+ }
+
+ private function stopServerOnWindows() {
+ // Unimplemented.
+ return 'failed';
+
+ }
+}
diff --git a/tests/selenium/SeleniumTestCase.php b/tests/selenium/SeleniumTestCase.php
new file mode 100644
index 00000000..7976c16a
--- /dev/null
+++ b/tests/selenium/SeleniumTestCase.php
@@ -0,0 +1,127 @@
+<?php
+include("SeleniumTestConstants.php");
+
+class SeleniumTestCase extends PHPUnit_Framework_TestCase { // PHPUnit_Extensions_SeleniumTestCase
+ protected $selenium;
+
+ public function setUp() {
+ set_time_limit( 60 );
+ $this->selenium = Selenium::getInstance();
+ }
+
+ public function tearDown() {
+
+ }
+
+ public function __call( $method, $args ) {
+ return call_user_func_array( array( $this->selenium, $method ), $args );
+ }
+
+ public function assertSeleniumAttributeEquals( $attribute, $value ) {
+ $attr = $this->getAttribute( $attribute );
+ $this->assertEquals( $attr, $value );
+ }
+
+ public function assertSeleniumHTMLContains( $element, $text ) {
+ $innerHTML = $this->getText( $element );
+ // or assertContains
+ $this->assertRegExp( "/$text/", $innerHTML );
+ }
+
+
+ /**
+ * Create a test fixture page if one does not exist
+ * @param $pageName The fixture page name. If none is supplied, it uses SeleniumTestConstants::WIKI_INTERNAL_LINK
+ */
+ function createTestPageIfMissing( $pageName = null ) {
+ if ( $pageName == null ) {
+ $pageName = SeleniumTestConstants::WIKI_INTERNAL_LINK;
+ }
+ $this->type( SeleniumTestConstants::INPUT_SEARCH_BOX, $pageName );
+ $this->click( SeleniumTestConstants::BUTTON_SEARCH );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->click( SeleniumTestConstants::LINK_START . $pageName );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $location = $this->getLocation() . "\n";
+ if ( strpos( $location, '&redlink=1') !== false ) {
+ $this->type( SeleniumTestConstants::TEXT_EDITOR, "Test fixture page. No real content here" );
+ $this->click( SeleniumTestConstants::BUTTON_SAVE );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertTrue( $this->isTextPresent( $pageName ),
+ $this->getText( SeleniumTestConstants::TEXT_PAGE_HEADING ) );
+ }
+ }
+
+ /**
+ * Create a test page using date as part of the name so that it is unique
+ * @param $pagePrefix The prefix to use for the page name. The current date will be appended to this to make it unique
+ * @param $watchThis Whether to add the page to my watchlist. Defaults to false.
+ */
+ function createNewTestPage( $pagePrefix, $watchThis = false ) {
+ $pageName = $pagePrefix . date("Ymd-His");
+ $this->type( SeleniumTestConstants::INPUT_SEARCH_BOX, $pageName );
+ $this->click( SeleniumTestConstants::BUTTON_SEARCH );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->click( SeleniumTestConstants::LINK_START . $pageName );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $location = $this->getLocation() . "\n";
+ $this->assertContains( '&redlink=1', $location ).
+ $this->type( SeleniumTestConstants::TEXT_EDITOR, "Test fixture page. No real content here" );
+ if ( $watchThis ) {
+ $this->click( "wpWatchthis" );
+ }
+ $this->click( SeleniumTestConstants::BUTTON_SAVE );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertTrue( $this->isTextPresent( $pageName ),
+ $this->getText( SeleniumTestConstants::TEXT_PAGE_HEADING ) );
+ return $pageName;
+ }
+
+ public function getExistingPage(){
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->type("searchInput", "new" );
+ $this->click("searchGoButton");
+ $this->waitForPageToLoad("30000");
+ }
+
+ public function getNewPage($pageName){
+
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->type("searchInput", $pageName );
+ $this->click("searchGoButton");
+ $this->waitForPageToLoad("30000");
+ $this->click("link=".$pageName);
+ $this->waitForPageToLoad("600000");
+
+
+ }
+ // Loading the mediawiki editor
+ public function loadWikiEditor(){
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ }
+
+ // Clear the content of the mediawiki editor
+ public function clearWikiEditor(){
+ $this->type("wpTextbox1", "");
+ }
+
+ // Click on the 'Show preview' button of the mediawiki editor
+ public function clickShowPreviewBtn(){
+ $this->click("wpPreview");
+ }
+
+ // Click on the 'Save Page' button of the mediawiki editor
+ public function clickSavePageBtn(){
+ $this->click("wpSave");
+ }
+
+ // Click on the 'Edit' link
+ public function clickEditLink(){
+ $this->click("link=Edit");
+ $this->waitForPageToLoad("30000");
+ }
+
+}
diff --git a/tests/selenium/SeleniumTestConsoleLogger.php b/tests/selenium/SeleniumTestConsoleLogger.php
new file mode 100644
index 00000000..b6f5496c
--- /dev/null
+++ b/tests/selenium/SeleniumTestConsoleLogger.php
@@ -0,0 +1,25 @@
+<?php
+
+class SeleniumTestConsoleLogger {
+ public function __construct() {
+ // Prepare testsuite for immediate output
+ @ini_set( 'zlib.output_compression', 0 );
+ @ini_set( 'implicit_flush', 1 );
+ for ( $i = 0; $i < ob_get_level(); $i++ ) {
+ ob_end_flush();
+ }
+ ob_implicit_flush( 1 );
+ }
+
+ public function write( $message, $mode = false ) {
+ $out = '';
+ // if ( $mode == SeleniumTestSuite::RESULT_OK ) $out .= '<font color="green">';
+ $out .= htmlentities( $message );
+ // if ( $mode == SeleniumTestSuite::RESULT_OK ) $out .= '</font>';
+ if ( $mode != SeleniumTestSuite::CONTINUE_LINE ) {
+ $out .= "\n";
+ }
+
+ echo $out;
+ }
+}
diff --git a/tests/selenium/SeleniumTestConstants.php b/tests/selenium/SeleniumTestConstants.php
new file mode 100644
index 00000000..1defb73c
--- /dev/null
+++ b/tests/selenium/SeleniumTestConstants.php
@@ -0,0 +1,24 @@
+<?php
+
+class SeleniumTestConstants {
+ const WIKI_TEST_WAIT_TIME = 3000; // Waiting time
+
+ //commonly used links
+ const LINK_MAIN_PAGE = 'link=Main page';
+ const LINK_RANDOM_PAGE = 'link=Random article';
+ const TEXT_PAGE_HEADING = 'firstHeading';
+
+ const LINK_START = 'link=';
+ const TEXT_EDITOR = 'wpTextbox1';
+ const LINK_PREVIEW = 'wpPreview';
+ const LINK_EDIT = 'link=Edit';
+
+ const WIKI_SEARCH_PAGE = 'Hair (musical)'; // Page name to search
+ const WIKI_TEXT_SEARCH = 'TV'; // Text to search
+ const WIKI_INTERNAL_LINK = 'Wikieditor-Fixture-Page'; // Exisiting page name to add as an internal tag
+
+ const INPUT_SEARCH_BOX = 'searchInput';
+ const BUTTON_SEARCH = 'mw-searchButton';
+ const BUTTON_SAVE = 'wpSave';
+}
+
diff --git a/tests/selenium/SeleniumTestHTMLLogger.php b/tests/selenium/SeleniumTestHTMLLogger.php
new file mode 100644
index 00000000..21332cf0
--- /dev/null
+++ b/tests/selenium/SeleniumTestHTMLLogger.php
@@ -0,0 +1,36 @@
+<?php
+
+class SeleniumTestHTMLLogger {
+ public function setHeaders() {
+ global $wgOut;
+ $wgOut->addHeadItem( 'selenium', '<style type="text/css">
+ .selenium pre {
+ overflow-x: auto; /* Use horizontal scroller if needed; for Firefox 2, not needed in Firefox 3 */
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ /* width: 99%; */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+ }
+ .selenium-success { color: green }
+ </style>' );
+ }
+
+ public function write( $message, $mode = false ) {
+ global $wgOut;
+ $out = '';
+ if ( $mode == SeleniumTestSuite::RESULT_OK ) {
+ $out .= '<span class="selenium-success">';
+ }
+ $out .= htmlspecialchars( $message );
+ if ( $mode == SeleniumTestSuite::RESULT_OK ) {
+ $out .= '</span>';
+ }
+ if ( $mode != SeleniumTestSuite::CONTINUE_LINE ) {
+ $out .= '<br />';
+ }
+
+ $wgOut->addHTML( $out );
+ }
+}
diff --git a/tests/selenium/SeleniumTestListener.php b/tests/selenium/SeleniumTestListener.php
new file mode 100644
index 00000000..9436f672
--- /dev/null
+++ b/tests/selenium/SeleniumTestListener.php
@@ -0,0 +1,68 @@
+<?php
+
+class SeleniumTestListener implements PHPUnit_Framework_TestListener {
+ private $logger;
+ private $tests_ok = 0;
+ private $tests_failed = 0;
+
+ public function __construct( $loggerInstance ) {
+ $this->logger = $loggerInstance;
+ }
+
+ public function addError( PHPUnit_Framework_Test $test, Exception $e, $time ) {
+ $this->logger->write( 'Error: ' . $e->getMessage() );
+ $this->tests_failed++;
+ }
+
+ public function addFailure( PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time )
+ {
+ $this->logger->write( 'Failed: ' . $e->getMessage() );
+ $this->tests_failed++;
+ }
+
+ public function addIncompleteTest( PHPUnit_Framework_Test $test, Exception $e, $time )
+ {
+ $this->logger->write( 'Incomplete.' );
+ $this->tests_failed++;
+ }
+
+ public function addSkippedTest( PHPUnit_Framework_Test $test, Exception $e, $time )
+ {
+ $this->logger->write( 'Skipped.' );
+ $this->tests_failed++;
+ }
+
+ public function startTest( PHPUnit_Framework_Test $test ) {
+ $this->logger->write(
+ 'Testing ' . $test->getName() . ' ... ',
+ SeleniumTestSuite::CONTINUE_LINE
+ );
+ }
+
+ public function endTest( PHPUnit_Framework_Test $test, $time ) {
+ if ( !$test->hasFailed() ) {
+ $this->logger->write( 'OK', SeleniumTestSuite::RESULT_OK );
+ $this->tests_ok++;
+ }
+ }
+
+ public function startTestSuite( PHPUnit_Framework_TestSuite $suite ) {
+ $this->logger->write( 'Testsuite ' . $suite->getName() . ' started.' );
+ $this->tests_ok = 0;
+ $this->tests_failed = 0;
+ }
+
+ public function endTestSuite( PHPUnit_Framework_TestSuite $suite ) {
+ $this->logger->write('Testsuite ' . $suite->getName() . ' ended.' );
+ if ( $this->tests_ok > 0 || $this->tests_failed > 0 ) {
+ $this->logger->write( ' OK: ' . $this->tests_ok . ' Failed: ' . $this->tests_failed );
+ }
+ $this->tests_ok = 0;
+ $this->tests_failed = 0;
+ }
+
+ public function statusMessage( $message ) {
+ $this->logger->write( $message );
+ }
+}
+
diff --git a/tests/selenium/SeleniumTestSuite.php b/tests/selenium/SeleniumTestSuite.php
new file mode 100644
index 00000000..81a630f8
--- /dev/null
+++ b/tests/selenium/SeleniumTestSuite.php
@@ -0,0 +1,57 @@
+<?php
+
+abstract class SeleniumTestSuite extends PHPUnit_Framework_TestSuite {
+ private $selenium;
+ private $isSetUp = false;
+ private $loginBeforeTests = true;
+ private $triggerClientTestResources = true;
+
+ // Do not add line break after test output
+ const CONTINUE_LINE = 1;
+ const RESULT_OK = 2;
+ const RESULT_ERROR = 3;
+
+ public abstract function addTests();
+
+ public function setUp() {
+ // Hack because because PHPUnit version 3.0.6 which is on prototype does not
+ // run setUp as part of TestSuite::run
+ if ( $this->isSetUp ) {
+ return;
+ }
+ $this->isSetUp = true;
+ $this->selenium = Selenium::getInstance();
+ $this->selenium->start();
+ if ( $this->triggerClientTestResources ) {
+ $this->selenium->open( $this->selenium->getUrl() . '/index.php?setupTestSuite=' . $this->getName() );
+ //wait a little longer for the db operation
+ $this->selenium->waitForPageToLoad( 6000 );
+ }
+ if ( $this->loginBeforeTests ) {
+ $this->login();
+ }
+ }
+
+ public function tearDown() {
+ if ( $this->triggerClientTestResources ) {
+ $this->selenium->open( $this->selenium->getUrl() . '/index.php?clearTestSuite=' . $this->getName() );
+ }
+ $this->selenium->stop();
+ }
+
+ public function login() {
+ $this->selenium->login();
+ }
+
+ public function loadPage( $title, $action ) {
+ $this->selenium->loadPage( $title, $action );
+ }
+
+ protected function setLoginBeforeTests( $loginBeforeTests = true ) {
+ $this->loginBeforeTests = $loginBeforeTests;
+ }
+
+ protected function setTriggerClientTestResources( $triggerClientTestResources = true ) {
+ $this->triggerClientTestResources = $triggerClientTestResources;
+ }
+}
diff --git a/tests/selenium/data/SimpleSeleniumTestDB.sql b/tests/selenium/data/SimpleSeleniumTestDB.sql
new file mode 100644
index 00000000..7944c45f
--- /dev/null
+++ b/tests/selenium/data/SimpleSeleniumTestDB.sql
@@ -0,0 +1,1454 @@
+-- MySQL dump 10.13 Distrib 5.1.41, for debian-linux-gnu (x86_64)
+--
+-- Host: localhost Database: test_wiki
+-- ------------------------------------------------------
+-- Server version 5.1.41-3ubuntu12.7
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `mw_archive`
+--
+
+DROP TABLE IF EXISTS `mw_archive`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_archive` (
+ `ar_namespace` int(11) NOT NULL DEFAULT '0',
+ `ar_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `ar_text` mediumblob NOT NULL,
+ `ar_comment` tinyblob NOT NULL,
+ `ar_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `ar_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `ar_timestamp` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `ar_minor_edit` tinyint(4) NOT NULL DEFAULT '0',
+ `ar_flags` tinyblob NOT NULL,
+ `ar_rev_id` int(10) unsigned DEFAULT NULL,
+ `ar_text_id` int(10) unsigned DEFAULT NULL,
+ `ar_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `ar_len` int(10) unsigned DEFAULT NULL,
+ `ar_page_id` int(10) unsigned DEFAULT NULL,
+ `ar_parent_id` int(10) unsigned DEFAULT NULL,
+ KEY `name_title_timestamp` (`ar_namespace`,`ar_title`,`ar_timestamp`),
+ KEY `usertext_timestamp` (`ar_user_text`,`ar_timestamp`),
+ KEY `ar_revid` (`ar_rev_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_archive`
+--
+
+LOCK TABLES `mw_archive` WRITE;
+/*!40000 ALTER TABLE `mw_archive` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_archive` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_category`
+--
+
+DROP TABLE IF EXISTS `mw_category`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_category` (
+ `cat_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `cat_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `cat_pages` int(11) NOT NULL DEFAULT '0',
+ `cat_subcats` int(11) NOT NULL DEFAULT '0',
+ `cat_files` int(11) NOT NULL DEFAULT '0',
+ `cat_hidden` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`cat_id`),
+ UNIQUE KEY `cat_title` (`cat_title`),
+ KEY `cat_pages` (`cat_pages`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_category`
+--
+
+LOCK TABLES `mw_category` WRITE;
+/*!40000 ALTER TABLE `mw_category` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_category` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_categorylinks`
+--
+
+DROP TABLE IF EXISTS `mw_categorylinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_categorylinks` (
+ `cl_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `cl_to` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `cl_sortkey` varbinary(230) NOT NULL DEFAULT '',
+ `cl_sortkey_prefix` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `cl_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `cl_collation` varbinary(32) NOT NULL DEFAULT '',
+ `cl_type` enum('page','subcat','file') NOT NULL DEFAULT 'page',
+ UNIQUE KEY `cl_from` (`cl_from`,`cl_to`),
+ KEY `cl_sortkey` (`cl_to`,`cl_type`,`cl_sortkey`,`cl_from`),
+ KEY `cl_timestamp` (`cl_to`,`cl_timestamp`),
+ KEY `cl_collation` (`cl_collation`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_categorylinks`
+--
+
+LOCK TABLES `mw_categorylinks` WRITE;
+/*!40000 ALTER TABLE `mw_categorylinks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_categorylinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_change_tag`
+--
+
+DROP TABLE IF EXISTS `mw_change_tag`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_change_tag` (
+ `ct_rc_id` int(11) DEFAULT NULL,
+ `ct_log_id` int(11) DEFAULT NULL,
+ `ct_rev_id` int(11) DEFAULT NULL,
+ `ct_tag` varchar(255) NOT NULL,
+ `ct_params` blob,
+ UNIQUE KEY `change_tag_rc_tag` (`ct_rc_id`,`ct_tag`),
+ UNIQUE KEY `change_tag_log_tag` (`ct_log_id`,`ct_tag`),
+ UNIQUE KEY `change_tag_rev_tag` (`ct_rev_id`,`ct_tag`),
+ KEY `change_tag_tag_id` (`ct_tag`,`ct_rc_id`,`ct_rev_id`,`ct_log_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_change_tag`
+--
+
+LOCK TABLES `mw_change_tag` WRITE;
+/*!40000 ALTER TABLE `mw_change_tag` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_change_tag` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_external_user`
+--
+
+DROP TABLE IF EXISTS `mw_external_user`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_external_user` (
+ `eu_local_id` int(10) unsigned NOT NULL,
+ `eu_external_id` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ PRIMARY KEY (`eu_local_id`),
+ UNIQUE KEY `eu_external_id` (`eu_external_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_external_user`
+--
+
+LOCK TABLES `mw_external_user` WRITE;
+/*!40000 ALTER TABLE `mw_external_user` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_external_user` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_externallinks`
+--
+
+DROP TABLE IF EXISTS `mw_externallinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_externallinks` (
+ `el_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `el_to` blob NOT NULL,
+ `el_index` blob NOT NULL,
+ KEY `el_from` (`el_from`,`el_to`(40)),
+ KEY `el_to` (`el_to`(60),`el_from`),
+ KEY `el_index` (`el_index`(60))
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_externallinks`
+--
+
+LOCK TABLES `mw_externallinks` WRITE;
+/*!40000 ALTER TABLE `mw_externallinks` DISABLE KEYS */;
+INSERT INTO `mw_externallinks` VALUES (1,'http://meta.wikimedia.org/wiki/Help:Contents','http://org.wikimedia.meta./wiki/Help:Contents'),(1,'http://www.mediawiki.org/wiki/Manual:Configuration_settings','http://org.mediawiki.www./wiki/Manual:Configuration_settings'),(1,'http://www.mediawiki.org/wiki/Manual:FAQ','http://org.mediawiki.www./wiki/Manual:FAQ'),(1,'https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce','https://org.wikimedia.lists./mailman/listinfo/mediawiki-announce');
+/*!40000 ALTER TABLE `mw_externallinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_filearchive`
+--
+
+DROP TABLE IF EXISTS `mw_filearchive`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_filearchive` (
+ `fa_id` int(11) NOT NULL AUTO_INCREMENT,
+ `fa_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `fa_archive_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT '',
+ `fa_storage_group` varbinary(16) DEFAULT NULL,
+ `fa_storage_key` varbinary(64) DEFAULT '',
+ `fa_deleted_user` int(11) DEFAULT NULL,
+ `fa_deleted_timestamp` binary(14) DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `fa_deleted_reason` text,
+ `fa_size` int(10) unsigned DEFAULT '0',
+ `fa_width` int(11) DEFAULT '0',
+ `fa_height` int(11) DEFAULT '0',
+ `fa_metadata` mediumblob,
+ `fa_bits` int(11) DEFAULT '0',
+ `fa_media_type` enum('UNKNOWN','BITMAP','DRAWING','AUDIO','VIDEO','MULTIMEDIA','OFFICE','TEXT','EXECUTABLE','ARCHIVE') DEFAULT NULL,
+ `fa_major_mime` enum('unknown','application','audio','image','text','video','message','model','multipart') DEFAULT 'unknown',
+ `fa_minor_mime` varbinary(100) DEFAULT 'unknown',
+ `fa_description` tinyblob,
+ `fa_user` int(10) unsigned DEFAULT '0',
+ `fa_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
+ `fa_timestamp` binary(14) DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `fa_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`fa_id`),
+ KEY `fa_name` (`fa_name`,`fa_timestamp`),
+ KEY `fa_storage_group` (`fa_storage_group`,`fa_storage_key`),
+ KEY `fa_deleted_timestamp` (`fa_deleted_timestamp`),
+ KEY `fa_user_timestamp` (`fa_user_text`,`fa_timestamp`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_filearchive`
+--
+
+LOCK TABLES `mw_filearchive` WRITE;
+/*!40000 ALTER TABLE `mw_filearchive` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_filearchive` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_hitcounter`
+--
+
+DROP TABLE IF EXISTS `mw_hitcounter`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_hitcounter` (
+ `hc_id` int(10) unsigned NOT NULL
+) ENGINE=MEMORY DEFAULT CHARSET=latin1 MAX_ROWS=25000;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_hitcounter`
+--
+
+LOCK TABLES `mw_hitcounter` WRITE;
+/*!40000 ALTER TABLE `mw_hitcounter` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_hitcounter` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_image`
+--
+
+DROP TABLE IF EXISTS `mw_image`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_image` (
+ `img_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `img_size` int(10) unsigned NOT NULL DEFAULT '0',
+ `img_width` int(11) NOT NULL DEFAULT '0',
+ `img_height` int(11) NOT NULL DEFAULT '0',
+ `img_metadata` mediumblob NOT NULL,
+ `img_bits` int(11) NOT NULL DEFAULT '0',
+ `img_media_type` enum('UNKNOWN','BITMAP','DRAWING','AUDIO','VIDEO','MULTIMEDIA','OFFICE','TEXT','EXECUTABLE','ARCHIVE') DEFAULT NULL,
+ `img_major_mime` enum('unknown','application','audio','image','text','video','message','model','multipart') NOT NULL DEFAULT 'unknown',
+ `img_minor_mime` varbinary(100) NOT NULL DEFAULT 'unknown',
+ `img_description` tinyblob NOT NULL,
+ `img_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `img_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `img_timestamp` varbinary(14) NOT NULL DEFAULT '',
+ `img_sha1` varbinary(32) NOT NULL DEFAULT '',
+ PRIMARY KEY (`img_name`),
+ KEY `img_usertext_timestamp` (`img_user_text`,`img_timestamp`),
+ KEY `img_size` (`img_size`),
+ KEY `img_timestamp` (`img_timestamp`),
+ KEY `img_sha1` (`img_sha1`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_image`
+--
+
+LOCK TABLES `mw_image` WRITE;
+/*!40000 ALTER TABLE `mw_image` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_image` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_imagelinks`
+--
+
+DROP TABLE IF EXISTS `mw_imagelinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_imagelinks` (
+ `il_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `il_to` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ UNIQUE KEY `il_from` (`il_from`,`il_to`),
+ UNIQUE KEY `il_to` (`il_to`,`il_from`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_imagelinks`
+--
+
+LOCK TABLES `mw_imagelinks` WRITE;
+/*!40000 ALTER TABLE `mw_imagelinks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_imagelinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_interwiki`
+--
+
+DROP TABLE IF EXISTS `mw_interwiki`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_interwiki` (
+ `iw_prefix` varchar(32) NOT NULL,
+ `iw_url` blob NOT NULL,
+ `iw_api` blob NOT NULL,
+ `iw_wikiid` varchar(64) NOT NULL,
+ `iw_local` tinyint(1) NOT NULL,
+ `iw_trans` tinyint(4) NOT NULL DEFAULT '0',
+ UNIQUE KEY `iw_prefix` (`iw_prefix`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_interwiki`
+--
+
+LOCK TABLES `mw_interwiki` WRITE;
+/*!40000 ALTER TABLE `mw_interwiki` DISABLE KEYS */;
+INSERT INTO `mw_interwiki` VALUES ('acronym','http://www.acronymfinder.com/af-query.asp?String=exact&Acronym=$1','','',0,0),('advogato','http://www.advogato.org/$1','','',0,0),('annotationwiki','http://www.seedwiki.com/page.cfm?wikiid=368&doc=$1','','',0,0),('arxiv','http://www.arxiv.org/abs/$1','','',0,0),('c2find','http://c2.com/cgi/wiki?FindPage&value=$1','','',0,0),('cache','http://www.google.com/search?q=cache:$1','','',0,0),('commons','http://commons.wikimedia.org/wiki/$1','','',0,0),('corpknowpedia','http://corpknowpedia.org/wiki/index.php/$1','','',0,0),('dictionary','http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=$1','','',0,0),('disinfopedia','http://www.disinfopedia.org/wiki.phtml?title=$1','','',0,0),('docbook','http://wiki.docbook.org/topic/$1','','',0,0),('doi','http://dx.doi.org/$1','','',0,0),('drumcorpswiki','http://www.drumcorpswiki.com/index.php/$1','','',0,0),('dwjwiki','http://www.suberic.net/cgi-bin/dwj/wiki.cgi?$1','','',0,0),('elibre','http://enciclopedia.us.es/index.php/$1','','',0,0),('emacswiki','http://www.emacswiki.org/cgi-bin/wiki.pl?$1','','',0,0),('foldoc','http://foldoc.org/?$1','','',0,0),('foxwiki','http://fox.wikis.com/wc.dll?Wiki~$1','','',0,0),('freebsdman','http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query=$1','','',0,0),('gej','http://www.esperanto.de/cgi-bin/aktivikio/wiki.pl?$1','','',0,0),('gentoo-wiki','http://gentoo-wiki.com/$1','','',0,0),('google','http://www.google.com/search?q=$1','','',0,0),('googlegroups','http://groups.google.com/groups?q=$1','','',0,0),('hammondwiki','http://www.dairiki.org/HammondWiki/$1','','',0,0),('hewikisource','http://he.wikisource.org/wiki/$1','','',1,0),('hrwiki','http://www.hrwiki.org/index.php/$1','','',0,0),('imdb','http://us.imdb.com/Title?$1','','',0,0),('jargonfile','http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1','','',0,0),('jspwiki','http://www.jspwiki.org/wiki/$1','','',0,0),('keiki','http://kei.ki/en/$1','','',0,0),('kmwiki','http://kmwiki.wikispaces.com/$1','','',0,0),('linuxwiki','http://linuxwiki.de/$1','','',0,0),('lojban','http://www.lojban.org/tiki/tiki-index.php?page=$1','','',0,0),('lqwiki','http://wiki.linuxquestions.org/wiki/$1','','',0,0),('lugkr','http://lug-kr.sourceforge.net/cgi-bin/lugwiki.pl?$1','','',0,0),('mathsongswiki','http://SeedWiki.com/page.cfm?wikiid=237&doc=$1','','',0,0),('meatball','http://www.usemod.com/cgi-bin/mb.pl?$1','','',0,0),('mediawikiwiki','http://www.mediawiki.org/wiki/$1','','',0,0),('mediazilla','https://bugzilla.wikimedia.org/$1','','',1,0),('memoryalpha','http://www.memory-alpha.org/en/index.php/$1','','',0,0),('metawiki','http://sunir.org/apps/meta.pl?$1','','',0,0),('metawikipedia','http://meta.wikimedia.org/wiki/$1','','',0,0),('moinmoin','http://purl.net/wiki/moin/$1','','',0,0),('mozillawiki','http://wiki.mozilla.org/index.php/$1','','',0,0),('mw','http://www.mediawiki.org/wiki/$1','','',0,0),('oeis','http://www.research.att.com/cgi-bin/access.cgi/as/njas/sequences/eisA.cgi?Anum=$1','','',0,0),('openfacts','http://openfacts.berlios.de/index.phtml?title=$1','','',0,0),('openwiki','http://openwiki.com/?$1','','',0,0),('pmeg','http://www.bertilow.com/pmeg/$1.php','','',0,0),('ppr','http://c2.com/cgi/wiki?$1','','',0,0),('pythoninfo','http://wiki.python.org/moin/$1','','',0,0),('rfc','http://www.rfc-editor.org/rfc/rfc$1.txt','','',0,0),('s23wiki','http://is-root.de/wiki/index.php/$1','','',0,0),('seattlewiki','http://seattle.wikia.com/wiki/$1','','',0,0),('seattlewireless','http://seattlewireless.net/?$1','','',0,0),('senseislibrary','http://senseis.xmp.net/?$1','','',0,0),('sourceforge','http://sourceforge.net/$1','','',0,0),('squeak','http://wiki.squeak.org/squeak/$1','','',0,0),('susning','http://www.susning.nu/$1','','',0,0),('svgwiki','http://wiki.svg.org/$1','','',0,0),('tavi','http://tavi.sourceforge.net/$1','','',0,0),('tejo','http://www.tejo.org/vikio/$1','','',0,0),('theopedia','http://www.theopedia.com/$1','','',0,0),('tmbw','http://www.tmbw.net/wiki/$1','','',0,0),('tmnet','http://www.technomanifestos.net/?$1','','',0,0),('tmwiki','http://www.EasyTopicMaps.com/?page=$1','','',0,0),('twiki','http://twiki.org/cgi-bin/view/$1','','',0,0),('uea','http://www.tejo.org/uea/$1','','',0,0),('unreal','http://wiki.beyondunreal.com/wiki/$1','','',0,0),('usemod','http://www.usemod.com/cgi-bin/wiki.pl?$1','','',0,0),('vinismo','http://vinismo.com/en/$1','','',0,0),('webseitzwiki','http://webseitz.fluxent.com/wiki/$1','','',0,0),('why','http://clublet.com/c/c/why?$1','','',0,0),('wiki','http://c2.com/cgi/wiki?$1','','',0,0),('wikia','http://www.wikia.com/wiki/$1','','',0,0),('wikibooks','http://en.wikibooks.org/wiki/$1','','',1,0),('wikicities','http://www.wikia.com/wiki/$1','','',0,0),('wikif1','http://www.wikif1.org/$1','','',0,0),('wikihow','http://www.wikihow.com/$1','','',0,0),('wikimedia','http://wikimediafoundation.org/wiki/$1','','',0,0),('wikinews','http://en.wikinews.org/wiki/$1','','',1,0),('wikinfo','http://www.wikinfo.org/index.php/$1','','',0,0),('wikipedia','http://en.wikipedia.org/wiki/$1','','',1,0),('wikiquote','http://en.wikiquote.org/wiki/$1','','',1,0),('wikisource','http://wikisource.org/wiki/$1','','',1,0),('wikispecies','http://species.wikimedia.org/wiki/$1','','',1,0),('wikitravel','http://wikitravel.org/en/$1','','',0,0),('wikiversity','http://en.wikiversity.org/wiki/$1','','',1,0),('wikt','http://en.wiktionary.org/wiki/$1','','',1,0),('wiktionary','http://en.wiktionary.org/wiki/$1','','',1,0),('wlug','http://www.wlug.org.nz/$1','','',0,0),('zwiki','http://zwiki.org/$1','','',0,0),('zzz wiki','http://wiki.zzz.ee/index.php/$1','','',0,0);
+/*!40000 ALTER TABLE `mw_interwiki` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_ipblocks`
+--
+
+DROP TABLE IF EXISTS `mw_ipblocks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_ipblocks` (
+ `ipb_id` int(11) NOT NULL AUTO_INCREMENT,
+ `ipb_address` tinyblob NOT NULL,
+ `ipb_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `ipb_by` int(10) unsigned NOT NULL DEFAULT '0',
+ `ipb_by_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `ipb_reason` tinyblob NOT NULL,
+ `ipb_timestamp` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `ipb_auto` tinyint(1) NOT NULL DEFAULT '0',
+ `ipb_anon_only` tinyint(1) NOT NULL DEFAULT '0',
+ `ipb_create_account` tinyint(1) NOT NULL DEFAULT '1',
+ `ipb_enable_autoblock` tinyint(1) NOT NULL DEFAULT '1',
+ `ipb_expiry` varbinary(14) NOT NULL DEFAULT '',
+ `ipb_range_start` tinyblob NOT NULL,
+ `ipb_range_end` tinyblob NOT NULL,
+ `ipb_deleted` tinyint(1) NOT NULL DEFAULT '0',
+ `ipb_block_email` tinyint(1) NOT NULL DEFAULT '0',
+ `ipb_allow_usertalk` tinyint(1) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ipb_id`),
+ UNIQUE KEY `ipb_address` (`ipb_address`(255),`ipb_user`,`ipb_auto`,`ipb_anon_only`),
+ KEY `ipb_user` (`ipb_user`),
+ KEY `ipb_range` (`ipb_range_start`(8),`ipb_range_end`(8)),
+ KEY `ipb_timestamp` (`ipb_timestamp`),
+ KEY `ipb_expiry` (`ipb_expiry`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_ipblocks`
+--
+
+LOCK TABLES `mw_ipblocks` WRITE;
+/*!40000 ALTER TABLE `mw_ipblocks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_ipblocks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_iwlinks`
+--
+
+DROP TABLE IF EXISTS `mw_iwlinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_iwlinks` (
+ `iwl_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `iwl_prefix` varbinary(20) NOT NULL DEFAULT '',
+ `iwl_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ UNIQUE KEY `iwl_from` (`iwl_from`,`iwl_prefix`,`iwl_title`),
+ UNIQUE KEY `iwl_prefix_title_from` (`iwl_prefix`,`iwl_title`,`iwl_from`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_iwlinks`
+--
+
+LOCK TABLES `mw_iwlinks` WRITE;
+/*!40000 ALTER TABLE `mw_iwlinks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_iwlinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_job`
+--
+
+DROP TABLE IF EXISTS `mw_job`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_job` (
+ `job_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `job_cmd` varbinary(60) NOT NULL DEFAULT '',
+ `job_namespace` int(11) NOT NULL,
+ `job_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `job_params` blob NOT NULL,
+ PRIMARY KEY (`job_id`),
+ KEY `job_cmd` (`job_cmd`,`job_namespace`,`job_title`,`job_params`(128))
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_job`
+--
+
+LOCK TABLES `mw_job` WRITE;
+/*!40000 ALTER TABLE `mw_job` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_job` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_l10n_cache`
+--
+
+DROP TABLE IF EXISTS `mw_l10n_cache`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_l10n_cache` (
+ `lc_lang` varbinary(32) NOT NULL,
+ `lc_key` varchar(255) NOT NULL,
+ `lc_value` mediumblob NOT NULL,
+ KEY `lc_lang_key` (`lc_lang`,`lc_key`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_l10n_cache`
+--
+
+LOCK TABLES `mw_l10n_cache` WRITE;
+/*!40000 ALTER TABLE `mw_l10n_cache` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_l10n_cache` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_langlinks`
+--
+
+DROP TABLE IF EXISTS `mw_langlinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_langlinks` (
+ `ll_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `ll_lang` varbinary(20) NOT NULL DEFAULT '',
+ `ll_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ UNIQUE KEY `ll_from` (`ll_from`,`ll_lang`),
+ KEY `ll_lang` (`ll_lang`,`ll_title`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_langlinks`
+--
+
+LOCK TABLES `mw_langlinks` WRITE;
+/*!40000 ALTER TABLE `mw_langlinks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_langlinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_log_search`
+--
+
+DROP TABLE IF EXISTS `mw_log_search`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_log_search` (
+ `ls_field` varbinary(32) NOT NULL,
+ `ls_value` varchar(255) NOT NULL,
+ `ls_log_id` int(10) unsigned NOT NULL DEFAULT '0',
+ UNIQUE KEY `ls_field_val` (`ls_field`,`ls_value`,`ls_log_id`),
+ KEY `ls_log_id` (`ls_log_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_log_search`
+--
+
+LOCK TABLES `mw_log_search` WRITE;
+/*!40000 ALTER TABLE `mw_log_search` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_log_search` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_logging`
+--
+
+DROP TABLE IF EXISTS `mw_logging`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_logging` (
+ `log_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `log_type` varbinary(32) NOT NULL DEFAULT '',
+ `log_action` varbinary(32) NOT NULL DEFAULT '',
+ `log_timestamp` binary(14) NOT NULL DEFAULT '19700101000000',
+ `log_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `log_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `log_namespace` int(11) NOT NULL DEFAULT '0',
+ `log_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `log_page` int(10) unsigned DEFAULT NULL,
+ `log_comment` varchar(255) NOT NULL DEFAULT '',
+ `log_params` blob NOT NULL,
+ `log_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`log_id`),
+ KEY `type_time` (`log_type`,`log_timestamp`),
+ KEY `user_time` (`log_user`,`log_timestamp`),
+ KEY `page_time` (`log_namespace`,`log_title`,`log_timestamp`),
+ KEY `times` (`log_timestamp`),
+ KEY `log_user_type_time` (`log_user`,`log_type`,`log_timestamp`),
+ KEY `log_page_id_time` (`log_page`,`log_timestamp`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_logging`
+--
+
+LOCK TABLES `mw_logging` WRITE;
+/*!40000 ALTER TABLE `mw_logging` DISABLE KEYS */;
+INSERT INTO `mw_logging` VALUES (1,'patrol','patrol','20110110173131',1,'WikiSysop',0,'TestResources',2,'','2\n0\n1',0);
+/*!40000 ALTER TABLE `mw_logging` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_math`
+--
+
+DROP TABLE IF EXISTS `mw_math`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_math` (
+ `math_inputhash` varbinary(16) NOT NULL,
+ `math_outputhash` varbinary(16) NOT NULL,
+ `math_html_conservativeness` tinyint(4) NOT NULL,
+ `math_html` text,
+ `math_mathml` text,
+ UNIQUE KEY `math_inputhash` (`math_inputhash`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_math`
+--
+
+LOCK TABLES `mw_math` WRITE;
+/*!40000 ALTER TABLE `mw_math` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_math` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_module_deps`
+--
+
+DROP TABLE IF EXISTS `mw_module_deps`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_module_deps` (
+ `md_module` varbinary(255) NOT NULL,
+ `md_skin` varbinary(32) NOT NULL,
+ `md_deps` mediumblob NOT NULL,
+ UNIQUE KEY `md_module_skin` (`md_module`,`md_skin`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_module_deps`
+--
+
+LOCK TABLES `mw_module_deps` WRITE;
+/*!40000 ALTER TABLE `mw_module_deps` DISABLE KEYS */;
+INSERT INTO `mw_module_deps` VALUES ('ext.vector.collapsibleNav','vector','[\"\\/home\\/pdhanda\\/deployment\\/extensions\\/Vector\\/modules\\/.\\/images\\/portal-break.png\",\"\\/home\\/pdhanda\\/deployment\\/extensions\\/Vector\\/modules\\/.\\/images\\/open.png\",\"\\/home\\/pdhanda\\/deployment\\/extensions\\/Vector\\/modules\\/.\\/images\\/closed-ltr.png\"]'),('jquery.wikiEditor','vector','[\"\\/home\\/pdhanda\\/deployment\\/extensions\\/WikiEditor\\/modules\\/.\\/images\\/toolbar\\/loading.gif\"]'),('jquery.wikiEditor.toolbar','vector','{\"0\":\"\\/home\\/pdhanda\\/deployment\\/extensions\\/WikiEditor\\/modules\\/.\\/images\\/toolbar\\/base.png\",\"1\":\"\\/home\\/pdhanda\\/deployment\\/extensions\\/WikiEditor\\/modules\\/.\\/images\\/toolbar\\/loading.gif\",\"2\":\"\\/home\\/pdhanda\\/deployment\\/extensions\\/WikiEditor\\/modules\\/.\\/images\\/toolbar\\/button-sprite.png\",\"3\":\"\\/home\\/pdhanda\\/deployment\\/extensions\\/WikiEditor\\/modules\\/.\\/images\\/toolbar\\/arrow-right.png\",\"4\":\"\\/home\\/pdhanda\\/deployment\\/extensions\\/WikiEditor\\/modules\\/.\\/images\\/toolbar\\/arrow-left.png\",\"5\":\"\\/home\\/pdhanda\\/deployment\\/extensions\\/WikiEditor\\/modules\\/.\\/images\\/toolbar\\/arrow-down.png\",\"7\":\"\\/home\\/pdhanda\\/deployment\\/extensions\\/WikiEditor\\/modules\\/.\\/images\\/toolbar\\/loading-small.gif\"}'),('mediawiki.legacy.shared','vector','[\"\\/home\\/pdhanda\\/deployment\\/skins\\/common\\/images\\/feed-icon.png\",\"\\/home\\/pdhanda\\/deployment\\/skins\\/common\\/images\\/remove.png\",\"\\/home\\/pdhanda\\/deployment\\/skins\\/common\\/images\\/add.png\",\"\\/home\\/pdhanda\\/deployment\\/skins\\/common\\/images\\/ajax-loader.gif\",\"\\/home\\/pdhanda\\/deployment\\/skins\\/common\\/images\\/spinner.gif\",\"\\/home\\/pdhanda\\/deployment\\/skins\\/common\\/images\\/help-question.gif\",\"\\/home\\/pdhanda\\/deployment\\/skins\\/common\\/images\\/help-question-hover.gif\"]'),('skins.vector','vector','{\"0\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/page-base.png\",\"1\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/border.png\",\"2\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/page-fade.png\",\"4\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/tab-break.png\",\"5\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/tab-normal-fade.png\",\"6\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/tab-current-fade.png\",\"8\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/arrow-down-icon.png\",\"11\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/search-fade.png\",\"12\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/portal-break.png\",\"14\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/preferences-break.png\",\"16\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/preferences-fade.png\",\"17\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/preferences-base.png\",\"18\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/bullet-icon.png\",\"19\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/external-link-ltr-icon.png\",\"20\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/lock-icon.png\",\"21\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/mail-icon.png\",\"22\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/news-icon.png\",\"23\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/file-icon.png\",\"24\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/talk-icon.png\",\"25\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/audio-icon.png\",\"26\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/video-icon.png\",\"27\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/document-icon.png\",\"28\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/user-icon.png\",\"29\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/watch-icons.png\",\"30\":\"\\/home\\/pdhanda\\/deployment\\/skins\\/vector\\/images\\/watch-icon-loading.gif\"}');
+/*!40000 ALTER TABLE `mw_module_deps` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_msg_resource`
+--
+
+DROP TABLE IF EXISTS `mw_msg_resource`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_msg_resource` (
+ `mr_resource` varbinary(255) NOT NULL,
+ `mr_lang` varbinary(32) NOT NULL,
+ `mr_blob` mediumblob NOT NULL,
+ `mr_timestamp` binary(14) NOT NULL,
+ UNIQUE KEY `mr_resource_lang` (`mr_resource`,`mr_lang`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_msg_resource`
+--
+
+LOCK TABLES `mw_msg_resource` WRITE;
+/*!40000 ALTER TABLE `mw_msg_resource` DISABLE KEYS */;
+INSERT INTO `mw_msg_resource` VALUES ('ext.vector.collapsibleNav','en','{\"vector-collapsiblenav-more\":\"More languages\"}','20110108005000'),('ext.vector.collapsibleTabs','en','{}','20110108005000'),('ext.vector.simpleSearch','en','{\"vector-simplesearch-search\":\"Search\",\"vector-simplesearch-containing\":\"containing...\"}','20110108005000'),('ext.wikiEditor','en','{}','20110110172914'),('ext.wikiEditor.toolbar','en','{\"wikieditor-toolbar-loading\":\"Loading...\",\"wikieditor-toolbar-tool-bold\":\"Bold\",\"wikieditor-toolbar-tool-bold-example\":\"Bold text\",\"wikieditor-toolbar-tool-italic\":\"Italic\",\"wikieditor-toolbar-tool-italic-example\":\"Italic text\",\"wikieditor-toolbar-tool-ilink\":\"Internal link\",\"wikieditor-toolbar-tool-ilink-example\":\"Link title\",\"wikieditor-toolbar-tool-xlink\":\"External link (remember http:\\/\\/ prefix)\",\"wikieditor-toolbar-tool-xlink-example\":\"http:\\/\\/www.example.com link title\",\"wikieditor-toolbar-tool-link\":\"Link\",\"wikieditor-toolbar-tool-link-title\":\"Insert link\",\"wikieditor-toolbar-tool-link-int\":\"To a wiki page\",\"wikieditor-toolbar-tool-link-int-target\":\"Target page or URL:\",\"wikieditor-toolbar-tool-link-int-target-tooltip\":\"Page title or URL\",\"wikieditor-toolbar-tool-link-int-text\":\"Text to display:\",\"wikieditor-toolbar-tool-link-int-text-tooltip\":\"Text to be displayed\",\"wikieditor-toolbar-tool-link-ext\":\"To an external web page\",\"wikieditor-toolbar-tool-link-ext-target\":\"Link URL:\",\"wikieditor-toolbar-tool-link-ext-text\":\"Link text:\",\"wikieditor-toolbar-tool-link-insert\":\"Insert link\",\"wikieditor-toolbar-tool-link-cancel\":\"Cancel\",\"wikieditor-toolbar-tool-link-int-target-status-exists\":\"Page exists\",\"wikieditor-toolbar-tool-link-int-target-status-notexists\":\"Page does not exist\",\"wikieditor-toolbar-tool-link-int-target-status-invalid\":\"Invalid title\",\"wikieditor-toolbar-tool-link-int-target-status-external\":\"External link\",\"wikieditor-toolbar-tool-link-int-target-status-loading\":\"Checking page existence...\",\"wikieditor-toolbar-tool-link-int-invalid\":\"The title you specified is invalid.\",\"wikieditor-toolbar-tool-link-lookslikeinternal\":\"The URL you specified looks like it was intended as a link to another wiki page.\\nDo you want to make it an internal link?\",\"wikieditor-toolbar-tool-link-lookslikeinternal-int\":\"Internal link\",\"wikieditor-toolbar-tool-link-lookslikeinternal-ext\":\"External link\",\"wikieditor-toolbar-tool-link-empty\":\"You did not enter anything to link to.\",\"wikieditor-toolbar-tool-file\":\"Embedded file\",\"wikieditor-toolbar-tool-file-pre\":\"$1{{ns:file}}:\",\"wikieditor-toolbar-tool-file-example\":\"Example.jpg\",\"wikieditor-toolbar-tool-reference\":\"Reference\",\"wikieditor-toolbar-tool-reference-title\":\"Insert reference\",\"wikieditor-toolbar-tool-reference-cancel\":\"Cancel\",\"wikieditor-toolbar-tool-reference-text\":\"Reference text\",\"wikieditor-toolbar-tool-reference-insert\":\"Insert\",\"wikieditor-toolbar-tool-reference-example\":\"Insert footnote text here\",\"wikieditor-toolbar-tool-signature\":\"Signature and timestamp\",\"wikieditor-toolbar-section-advanced\":\"Advanced\",\"wikieditor-toolbar-tool-heading\":\"Heading\",\"wikieditor-toolbar-tool-heading-1\":\"Level 1\",\"wikieditor-toolbar-tool-heading-2\":\"Level 2\",\"wikieditor-toolbar-tool-heading-3\":\"Level 3\",\"wikieditor-toolbar-tool-heading-4\":\"Level 4\",\"wikieditor-toolbar-tool-heading-5\":\"Level 5\",\"wikieditor-toolbar-tool-heading-example\":\"Heading text\",\"wikieditor-toolbar-group-format\":\"Format\",\"wikieditor-toolbar-tool-ulist\":\"Bulleted list\",\"wikieditor-toolbar-tool-ulist-example\":\"Bulleted list item\",\"wikieditor-toolbar-tool-olist\":\"Numbered list\",\"wikieditor-toolbar-tool-olist-example\":\"Numbered list item\",\"wikieditor-toolbar-tool-indent\":\"Indentation\",\"wikieditor-toolbar-tool-indent-example\":\"Indented line\",\"wikieditor-toolbar-tool-nowiki\":\"No wiki formatting\",\"wikieditor-toolbar-tool-nowiki-example\":\"Insert non-formatted text here\",\"wikieditor-toolbar-tool-redirect\":\"Redirect\",\"wikieditor-toolbar-tool-redirect-example\":\"Target page name\",\"wikieditor-toolbar-tool-big\":\"Big\",\"wikieditor-toolbar-tool-big-example\":\"Big text\",\"wikieditor-toolbar-tool-small\":\"Small\",\"wikieditor-toolbar-tool-small-example\":\"Small text\",\"wikieditor-toolbar-tool-superscript\":\"Superscript\",\"wikieditor-toolbar-tool-superscript-example\":\"Superscript text\",\"wikieditor-toolbar-tool-subscript\":\"Subscript\",\"wikieditor-toolbar-tool-subscript-example\":\"Subscript text\",\"wikieditor-toolbar-group-insert\":\"Insert\",\"wikieditor-toolbar-tool-gallery\":\"Picture gallery\",\"wikieditor-toolbar-tool-gallery-example\":\"{{ns:file}}:Example.jpg|Caption1\\n{{ns:file}}:Example.jpg|Caption2\",\"wikieditor-toolbar-tool-newline\":\"New line\",\"wikieditor-toolbar-tool-table\":\"Table\",\"wikieditor-toolbar-tool-table-example-old\":\"-\\n! header 1\\n! header 2\\n! header 3\\n|-\\n| row 1, cell 1\\n| row 1, cell 2\\n| row 1, cell 3\\n|-\\n| row 2, cell 1\\n| row 2, cell 2\\n| row 2, cell 3\",\"wikieditor-toolbar-tool-table-example-cell-text\":\"Cell text\",\"wikieditor-toolbar-tool-table-example\":\"Example\",\"wikieditor-toolbar-tool-table-example-header\":\"Header text\",\"wikieditor-toolbar-tool-table-title\":\"Insert table\",\"wikieditor-toolbar-tool-table-dimensions-rows\":\"Rows\",\"wikieditor-toolbar-tool-table-dimensions-columns\":\"Columns\",\"wikieditor-toolbar-tool-table-dimensions-header\":\"Add header row\",\"wikieditor-toolbar-tool-table-wikitable\":\"Style with borders\",\"wikieditor-toolbar-tool-table-sortable\":\"Make table sortable\",\"wikieditor-toolbar-tool-table-insert\":\"Insert\",\"wikieditor-toolbar-tool-table-cancel\":\"Cancel\",\"wikieditor-toolbar-tool-table-example-text\":\"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut nec purus diam. Sed aliquam imperdiet nunc quis lacinia. Donec rutrum consectetur placerat. Sed volutpat neque non purus faucibus id ultricies enim euismod.\",\"wikieditor-toolbar-tool-table-toomany\":\"Inserting a table with more than $1 cells is not possible with this dialog.\",\"wikieditor-toolbar-tool-table-invalidnumber\":\"You have not entered a valid number of rows or columns.\",\"wikieditor-toolbar-tool-table-zero\":\"You cannot insert a table with zero rows or columns.\",\"wikieditor-toolbar-tool-replace\":\"Search and replace\",\"wikieditor-toolbar-tool-replace-title\":\"Search and replace\",\"wikieditor-toolbar-tool-replace-search\":\"Search for:\",\"wikieditor-toolbar-tool-replace-replace\":\"Replace with:\",\"wikieditor-toolbar-tool-replace-case\":\"Match case\",\"wikieditor-toolbar-tool-replace-regex\":\"Treat search string as a regular expression\",\"wikieditor-toolbar-tool-replace-button-findnext\":\"Find next\",\"wikieditor-toolbar-tool-replace-button-replacenext\":\"Replace next\",\"wikieditor-toolbar-tool-replace-button-replaceall\":\"Replace all\",\"wikieditor-toolbar-tool-replace-close\":\"Close\",\"wikieditor-toolbar-tool-replace-nomatch\":\"Your search did not match anything.\",\"wikieditor-toolbar-tool-replace-success\":\"$1 replacement(s) made.\",\"wikieditor-toolbar-tool-replace-emptysearch\":\"You did not enter anything to search for.\",\"wikieditor-toolbar-tool-replace-invalidregex\":\"The regular expression you entered is invalid: $1\",\"wikieditor-toolbar-section-characters\":\"Special characters\",\"wikieditor-toolbar-characters-page-latin\":\"Latin\",\"wikieditor-toolbar-characters-page-latinextended\":\"Latin extended\",\"wikieditor-toolbar-characters-page-ipa\":\"IPA\",\"wikieditor-toolbar-characters-page-symbols\":\"Symbols\",\"wikieditor-toolbar-characters-page-greek\":\"Greek\",\"wikieditor-toolbar-characters-page-cyrillic\":\"Cyrillic\",\"wikieditor-toolbar-characters-page-arabic\":\"Arabic\",\"wikieditor-toolbar-characters-page-persian\":\"Persian\",\"wikieditor-toolbar-characters-page-hebrew\":\"Hebrew\",\"wikieditor-toolbar-characters-page-bangla\":\"Bangla\",\"wikieditor-toolbar-characters-page-telugu\":\"Telugu\",\"wikieditor-toolbar-characters-page-sinhala\":\"Sinhala\",\"wikieditor-toolbar-characters-page-gujarati\":\"Gujarati\",\"wikieditor-toolbar-characters-page-thai\":\"Thai\",\"wikieditor-toolbar-characters-page-lao\":\"Lao\",\"wikieditor-toolbar-characters-page-khmer\":\"Khmer\",\"wikieditor-toolbar-section-help\":\"Help\",\"wikieditor-toolbar-help-heading-description\":\"Description\",\"wikieditor-toolbar-help-heading-syntax\":\"What you type\",\"wikieditor-toolbar-help-heading-result\":\"What you get\",\"wikieditor-toolbar-help-page-format\":\"Formatting\",\"wikieditor-toolbar-help-page-link\":\"Links\",\"wikieditor-toolbar-help-page-heading\":\"Headings\",\"wikieditor-toolbar-help-page-list\":\"Lists\",\"wikieditor-toolbar-help-page-file\":\"Files\",\"wikieditor-toolbar-help-page-reference\":\"References\",\"wikieditor-toolbar-help-page-discussion\":\"Discussion\",\"wikieditor-toolbar-help-content-bold-description\":\"Bold\",\"wikieditor-toolbar-help-content-bold-syntax\":\"\'\'\'Bold text\'\'\'\",\"wikieditor-toolbar-help-content-bold-result\":\"<strong>Bold text<\\/strong>\",\"wikieditor-toolbar-help-content-italic-description\":\"Italic\",\"wikieditor-toolbar-help-content-italic-syntax\":\"\'\'Italic text\'\'\",\"wikieditor-toolbar-help-content-italic-result\":\"<em>Italic text<\\/em>\",\"wikieditor-toolbar-help-content-bolditalic-description\":\"Bold &amp; italic\",\"wikieditor-toolbar-help-content-bolditalic-syntax\":\"\'\'\'\'\'Bold &amp; italic text\'\'\'\'\'\",\"wikieditor-toolbar-help-content-bolditalic-result\":\"<strong><em>Bold &amp; italic text<\\/em><\\/strong>\",\"wikieditor-toolbar-help-content-ilink-description\":\"Internal link\",\"wikieditor-toolbar-help-content-ilink-syntax\":\"[[Page title|Link label]]<br \\/>[[Page title]]\",\"wikieditor-toolbar-help-content-ilink-result\":\"<a href=\'#\'>Link label<\\/a><br \\/><a href=\'#\'>Page title<\\/a>\",\"wikieditor-toolbar-help-content-xlink-description\":\"External link\",\"wikieditor-toolbar-help-content-xlink-syntax\":\"[http:\\/\\/www.example.org Link label]<br \\/>[http:\\/\\/www.example.org]<br \\/>http:\\/\\/www.example.org\",\"wikieditor-toolbar-help-content-xlink-result\":\"<a href=\'#\' class=\'external\'>Link label<\\/a><br \\/><a href=\'#\' class=\'external autonumber\'>[1]<\\/a><br \\/><a href=\'#\' class=\'external\'>http:\\/\\/www.example.org<\\/a>\",\"wikieditor-toolbar-help-content-heading1-description\":\"&lt;wikieditor-toolbar-help-content-heading1-description&gt;\",\"wikieditor-toolbar-help-content-heading1-syntax\":\"&lt;wikieditor-toolbar-help-content-heading1-syntax&gt;\",\"wikieditor-toolbar-help-content-heading1-result\":\"&lt;wikieditor-toolbar-help-content-heading1-result&gt;\",\"wikieditor-toolbar-help-content-heading2-description\":\"2nd level heading\",\"wikieditor-toolbar-help-content-heading2-syntax\":\"== Heading text ==\",\"wikieditor-toolbar-help-content-heading2-result\":\"<h2>Heading text<\\/h2>\",\"wikieditor-toolbar-help-content-heading3-description\":\"3rd level heading\",\"wikieditor-toolbar-help-content-heading3-syntax\":\"=== Heading text ===\",\"wikieditor-toolbar-help-content-heading3-result\":\"<h3>Heading text<\\/h3>\",\"wikieditor-toolbar-help-content-heading4-description\":\"4th level heading\",\"wikieditor-toolbar-help-content-heading4-syntax\":\"==== Heading text ====\",\"wikieditor-toolbar-help-content-heading4-result\":\"<h4>Heading text<\\/h4>\",\"wikieditor-toolbar-help-content-heading5-description\":\"5th level heading\",\"wikieditor-toolbar-help-content-heading5-syntax\":\"===== Heading text =====\",\"wikieditor-toolbar-help-content-heading5-result\":\"<h5>Heading text<\\/h5>\",\"wikieditor-toolbar-help-content-ulist-description\":\"Bulleted list\",\"wikieditor-toolbar-help-content-ulist-syntax\":\"* List item<br \\/>* List item\",\"wikieditor-toolbar-help-content-ulist-result\":\"<ul><li>List item<\\/li><li>List item<\\/li><\\/ul>\",\"wikieditor-toolbar-help-content-olist-description\":\"Numbered list\",\"wikieditor-toolbar-help-content-olist-syntax\":\"# List item<br \\/># List item\",\"wikieditor-toolbar-help-content-olist-result\":\"<ol><li>List item<\\/li><li>List item<\\/li><\\/ol>\",\"wikieditor-toolbar-help-content-file-description\":\"Embedded file\",\"wikieditor-toolbar-help-content-file-syntax\":\"[[{{ns:file}}:Example.png|thumb|Caption text]]\",\"wikieditor-toolbar-help-content-file-result\":\"<div style=\'width:104px;\' class=\'thumbinner\'><a title=\'Caption text\' class=\'image\' href=\'#\'><img height=\'50\' width=\'100\' border=\'0\' class=\'thumbimage\' src=\'extensions\\/UsabilityInitiative\\/images\\/wikiEditor\\/toolbar\\/example-image.png\' alt=\'\'\\/><\\/a><div class=\'thumbcaption\'><div class=\'magnify\'><a title=\'Enlarge\' class=\'internal\' href=\'#\'><img height=\'11\' width=\'15\' alt=\'\' src=\'$1\\/common\\/images\\/magnify-clip.png\'\\/><\\/a><\\/div>Caption text<\\/div><\\/div>\",\"wikieditor-toolbar-help-content-reference-description\":\"Reference\",\"wikieditor-toolbar-help-content-reference-syntax\":\"Page text.&lt;ref name=\\\"test\\\"&gt;[http:\\/\\/www.example.org Link text], additional text.&lt;\\/ref&gt;\",\"wikieditor-toolbar-help-content-reference-result\":\"Page text.<sup><a href=\'#\'>[1]<\\/a><\\/sup>\",\"wikieditor-toolbar-help-content-rereference-description\":\"Additional use of same reference\",\"wikieditor-toolbar-help-content-rereference-syntax\":\"&lt;ref name=\\\"test\\\" \\/&gt;\",\"wikieditor-toolbar-help-content-rereference-result\":\"Page text.<sup><a href=\'#\'>[1]<\\/a><\\/sup>\",\"wikieditor-toolbar-help-content-showreferences-description\":\"Display references\",\"wikieditor-toolbar-help-content-showreferences-syntax\":\"&lt;references \\/&gt;\",\"wikieditor-toolbar-help-content-showreferences-result\":\"<ol class=\'references\'><li id=\'cite_note-test-0\'><b><a title=\'\' href=\'#\'>^<\\/a><\\/b> <a rel=\'nofollow\' title=\'http:\\/\\/www.example.org\' class=\'external text\' href=\'#\'>Link text<\\/a>, additional text.<\\/li><\\/ol>\",\"wikieditor-toolbar-help-content-signaturetimestamp-description\":\"Signature with timestamp\",\"wikieditor-toolbar-help-content-signaturetimestamp-syntax\":\"~~~~\",\"wikieditor-toolbar-help-content-signaturetimestamp-result\":\"<a href=\'#\' title=\'{{#special:mypage}}\'>Username<\\/a> (<a href=\'#\' title=\'{{#special:mytalk}}\'>talk<\\/a>) 15:54, 10 June 2009 (UTC)\",\"wikieditor-toolbar-help-content-signature-description\":\"Signature\",\"wikieditor-toolbar-help-content-signature-syntax\":\"~~~\",\"wikieditor-toolbar-help-content-signature-result\":\"<a href=\'#\' title=\'{{#special:mypage}}\'>Username<\\/a> (<a href=\'#\' title=\'{{#special:mytalk}}\'>talk<\\/a>)\",\"wikieditor-toolbar-help-content-indent-description\":\"Indent\",\"wikieditor-toolbar-help-content-indent-syntax\":\"Normal text<br \\/>:Indented text<br \\/>::Indented text\",\"wikieditor-toolbar-help-content-indent-result\":\"Normal text<dl><dd>Indented text<dl><dd>Indented text<\\/dd><\\/dl><\\/dd><\\/dl>\"}','20110110172914'),('jquery.async','en','{}','20110110172915'),('jquery.autoEllipsis','en','{}','20110110172915'),('jquery.checkboxShiftClick','en','{}','20110110172915'),('jquery.client','en','{}','20110110172915'),('jquery.collapsibleTabs','en','{}','20110110172915'),('jquery.cookie','en','{}','20110110172915'),('jquery.delayedBind','en','{}','20110110172915'),('jquery.highlightText','en','{}','20110110172915'),('jquery.makeCollapsible','en','{\"collapsible-expand\":\"Expand\",\"collapsible-collapse\":\"Collapse\"}','20110110172915'),('jquery.placeholder','en','{}','20110110172915'),('jquery.suggestions','en','{}','20110110172915'),('jquery.tabIndex','en','{}','20110110172915'),('jquery.textSelection','en','{}','20110110172915'),('jquery.wikiEditor','en','{\"wikieditor-wikitext-tab\":\"Wikitext\",\"wikieditor-loading\":\"Loading\"}','20110110172914'),('jquery.wikiEditor.toolbar','en','{}','20110110172914'),('mediawiki.action.watch.ajax','en','{}','20110110172915'),('mediawiki.language','en','{}','20110110172915'),('mediawiki.legacy.ajax','en','{\"watch\":\"Watch\",\"unwatch\":\"Unwatch\",\"watching\":\"Watching...\",\"unwatching\":\"Unwatching...\",\"tooltip-ca-watch\":\"Add this page to your watchlist\",\"tooltip-ca-unwatch\":\"Remove this page from your watchlist\"}','20110110172915'),('mediawiki.legacy.edit','en','{}','20110110172915'),('mediawiki.legacy.wikibits','en','{\"showtoc\":\"show\",\"hidetoc\":\"hide\"}','20110110172915'),('mediawiki.util','en','{}','20110110172915');
+/*!40000 ALTER TABLE `mw_msg_resource` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_msg_resource_links`
+--
+
+DROP TABLE IF EXISTS `mw_msg_resource_links`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_msg_resource_links` (
+ `mrl_resource` varbinary(255) NOT NULL,
+ `mrl_message` varbinary(255) NOT NULL,
+ UNIQUE KEY `mrl_message_resource` (`mrl_message`,`mrl_resource`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_msg_resource_links`
+--
+
+LOCK TABLES `mw_msg_resource_links` WRITE;
+/*!40000 ALTER TABLE `mw_msg_resource_links` DISABLE KEYS */;
+INSERT INTO `mw_msg_resource_links` VALUES ('jquery.makeCollapsible','collapsible-collapse'),('jquery.makeCollapsible','collapsible-expand'),('mediawiki.legacy.wikibits','hidetoc'),('mediawiki.legacy.wikibits','showtoc'),('mediawiki.legacy.ajax','tooltip-ca-unwatch'),('mediawiki.legacy.ajax','tooltip-ca-watch'),('mediawiki.legacy.ajax','unwatch'),('mediawiki.legacy.ajax','unwatching'),('ext.vector.collapsibleNav','vector-collapsiblenav-more'),('ext.vector.simpleSearch','vector-simplesearch-containing'),('ext.vector.simpleSearch','vector-simplesearch-search'),('mediawiki.legacy.ajax','watch'),('mediawiki.legacy.ajax','watching'),('jquery.wikiEditor','wikieditor-loading'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-arabic'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-bangla'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-cyrillic'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-greek'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-gujarati'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-hebrew'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-ipa'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-khmer'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-lao'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-latin'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-latinextended'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-persian'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-sinhala'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-symbols'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-telugu'),('ext.wikiEditor.toolbar','wikieditor-toolbar-characters-page-thai'),('ext.wikiEditor.toolbar','wikieditor-toolbar-group-format'),('ext.wikiEditor.toolbar','wikieditor-toolbar-group-insert'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-bold-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-bold-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-bold-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-bolditalic-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-bolditalic-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-bolditalic-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-file-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-file-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-file-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading1-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading1-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading1-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading2-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading2-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading2-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading3-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading3-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading3-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading4-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading4-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading4-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading5-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading5-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-heading5-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-ilink-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-ilink-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-ilink-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-indent-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-indent-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-indent-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-italic-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-italic-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-italic-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-olist-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-olist-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-olist-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-reference-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-reference-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-reference-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-rereference-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-rereference-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-rereference-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-showreferences-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-showreferences-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-showreferences-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-signature-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-signature-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-signature-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-signaturetimestamp-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-signaturetimestamp-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-signaturetimestamp-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-ulist-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-ulist-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-ulist-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-xlink-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-xlink-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-content-xlink-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-heading-description'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-heading-result'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-heading-syntax'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-page-discussion'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-page-file'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-page-format'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-page-heading'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-page-link'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-page-list'),('ext.wikiEditor.toolbar','wikieditor-toolbar-help-page-reference'),('ext.wikiEditor.toolbar','wikieditor-toolbar-loading'),('ext.wikiEditor.toolbar','wikieditor-toolbar-section-advanced'),('ext.wikiEditor.toolbar','wikieditor-toolbar-section-characters'),('ext.wikiEditor.toolbar','wikieditor-toolbar-section-help'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-big'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-big-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-bold'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-bold-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-file'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-file-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-file-pre'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-gallery'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-gallery-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-heading'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-heading-1'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-heading-2'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-heading-3'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-heading-4'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-heading-5'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-heading-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-ilink'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-ilink-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-indent'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-indent-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-italic'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-italic-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-cancel'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-empty'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-ext'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-ext-target'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-ext-text'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-insert'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-int'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-int-invalid'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-int-target'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-int-target-status-exists'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-int-target-status-external'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-int-target-status-invalid'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-int-target-status-loading'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-int-target-status-notexists'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-int-target-tooltip'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-int-text'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-int-text-tooltip'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-lookslikeinternal'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-lookslikeinternal-ext'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-lookslikeinternal-int'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-link-title'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-newline'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-nowiki'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-nowiki-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-olist'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-olist-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-redirect'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-redirect-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-reference'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-reference-cancel'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-reference-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-reference-insert'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-reference-text'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-reference-title'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-button-findnext'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-button-replaceall'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-button-replacenext'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-case'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-close'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-emptysearch'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-invalidregex'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-nomatch'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-regex'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-replace'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-search'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-success'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-replace-title'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-signature'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-small'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-small-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-subscript'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-subscript-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-superscript'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-superscript-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-cancel'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-dimensions-columns'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-dimensions-header'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-dimensions-rows'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-example-cell-text'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-example-header'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-example-old'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-example-text'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-insert'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-invalidnumber'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-sortable'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-title'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-toomany'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-wikitable'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-table-zero'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-ulist'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-ulist-example'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-xlink'),('ext.wikiEditor.toolbar','wikieditor-toolbar-tool-xlink-example'),('jquery.wikiEditor','wikieditor-wikitext-tab');
+/*!40000 ALTER TABLE `mw_msg_resource_links` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_objectcache`
+--
+
+DROP TABLE IF EXISTS `mw_objectcache`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_objectcache` (
+ `keyname` varbinary(255) NOT NULL DEFAULT '',
+ `value` mediumblob,
+ `exptime` datetime DEFAULT NULL,
+ PRIMARY KEY (`keyname`),
+ KEY `exptime` (`exptime`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_objectcache`
+--
+
+LOCK TABLES `mw_objectcache` WRITE;
+/*!40000 ALTER TABLE `mw_objectcache` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_objectcache` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_oldimage`
+--
+
+DROP TABLE IF EXISTS `mw_oldimage`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_oldimage` (
+ `oi_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `oi_archive_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `oi_size` int(10) unsigned NOT NULL DEFAULT '0',
+ `oi_width` int(11) NOT NULL DEFAULT '0',
+ `oi_height` int(11) NOT NULL DEFAULT '0',
+ `oi_bits` int(11) NOT NULL DEFAULT '0',
+ `oi_description` tinyblob NOT NULL,
+ `oi_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `oi_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `oi_timestamp` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `oi_metadata` mediumblob NOT NULL,
+ `oi_media_type` enum('UNKNOWN','BITMAP','DRAWING','AUDIO','VIDEO','MULTIMEDIA','OFFICE','TEXT','EXECUTABLE','ARCHIVE') DEFAULT NULL,
+ `oi_major_mime` enum('unknown','application','audio','image','text','video','message','model','multipart') NOT NULL DEFAULT 'unknown',
+ `oi_minor_mime` varbinary(100) NOT NULL DEFAULT 'unknown',
+ `oi_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `oi_sha1` varbinary(32) NOT NULL DEFAULT '',
+ KEY `oi_usertext_timestamp` (`oi_user_text`,`oi_timestamp`),
+ KEY `oi_name_timestamp` (`oi_name`,`oi_timestamp`),
+ KEY `oi_name_archive_name` (`oi_name`,`oi_archive_name`(14)),
+ KEY `oi_sha1` (`oi_sha1`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_oldimage`
+--
+
+LOCK TABLES `mw_oldimage` WRITE;
+/*!40000 ALTER TABLE `mw_oldimage` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_oldimage` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_page`
+--
+
+DROP TABLE IF EXISTS `mw_page`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_page` (
+ `page_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `page_namespace` int(11) NOT NULL,
+ `page_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `page_restrictions` tinyblob NOT NULL,
+ `page_counter` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `page_is_redirect` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `page_is_new` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `page_random` double unsigned NOT NULL,
+ `page_touched` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `page_latest` int(10) unsigned NOT NULL,
+ `page_len` int(10) unsigned NOT NULL,
+ PRIMARY KEY (`page_id`),
+ UNIQUE KEY `name_title` (`page_namespace`,`page_title`),
+ KEY `page_random` (`page_random`),
+ KEY `page_len` (`page_len`)
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_page`
+--
+
+LOCK TABLES `mw_page` WRITE;
+/*!40000 ALTER TABLE `mw_page` DISABLE KEYS */;
+INSERT INTO `mw_page` VALUES (1,0,'Main_Page','',3,0,1,0.045389076294,'20110107184113',1,438),(2,0,'TestResources','',0,0,1,0.227355086893,'20110110173217',2,57);
+/*!40000 ALTER TABLE `mw_page` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_page_props`
+--
+
+DROP TABLE IF EXISTS `mw_page_props`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_page_props` (
+ `pp_page` int(11) NOT NULL,
+ `pp_propname` varbinary(60) NOT NULL,
+ `pp_value` blob NOT NULL,
+ UNIQUE KEY `pp_page_propname` (`pp_page`,`pp_propname`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_page_props`
+--
+
+LOCK TABLES `mw_page_props` WRITE;
+/*!40000 ALTER TABLE `mw_page_props` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_page_props` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_page_restrictions`
+--
+
+DROP TABLE IF EXISTS `mw_page_restrictions`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_page_restrictions` (
+ `pr_page` int(11) NOT NULL,
+ `pr_type` varbinary(60) NOT NULL,
+ `pr_level` varbinary(60) NOT NULL,
+ `pr_cascade` tinyint(4) NOT NULL,
+ `pr_user` int(11) DEFAULT NULL,
+ `pr_expiry` varbinary(14) DEFAULT NULL,
+ `pr_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY (`pr_id`),
+ UNIQUE KEY `pr_pagetype` (`pr_page`,`pr_type`),
+ KEY `pr_typelevel` (`pr_type`,`pr_level`),
+ KEY `pr_level` (`pr_level`),
+ KEY `pr_cascade` (`pr_cascade`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_page_restrictions`
+--
+
+LOCK TABLES `mw_page_restrictions` WRITE;
+/*!40000 ALTER TABLE `mw_page_restrictions` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_page_restrictions` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_pagelinks`
+--
+
+DROP TABLE IF EXISTS `mw_pagelinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_pagelinks` (
+ `pl_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `pl_namespace` int(11) NOT NULL DEFAULT '0',
+ `pl_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ UNIQUE KEY `pl_from` (`pl_from`,`pl_namespace`,`pl_title`),
+ UNIQUE KEY `pl_namespace` (`pl_namespace`,`pl_title`,`pl_from`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_pagelinks`
+--
+
+LOCK TABLES `mw_pagelinks` WRITE;
+/*!40000 ALTER TABLE `mw_pagelinks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_pagelinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_protected_titles`
+--
+
+DROP TABLE IF EXISTS `mw_protected_titles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_protected_titles` (
+ `pt_namespace` int(11) NOT NULL,
+ `pt_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `pt_user` int(10) unsigned NOT NULL,
+ `pt_reason` tinyblob,
+ `pt_timestamp` binary(14) NOT NULL,
+ `pt_expiry` varbinary(14) NOT NULL DEFAULT '',
+ `pt_create_perm` varbinary(60) NOT NULL,
+ UNIQUE KEY `pt_namespace_title` (`pt_namespace`,`pt_title`),
+ KEY `pt_timestamp` (`pt_timestamp`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_protected_titles`
+--
+
+LOCK TABLES `mw_protected_titles` WRITE;
+/*!40000 ALTER TABLE `mw_protected_titles` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_protected_titles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_querycache`
+--
+
+DROP TABLE IF EXISTS `mw_querycache`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_querycache` (
+ `qc_type` varbinary(32) NOT NULL,
+ `qc_value` int(10) unsigned NOT NULL DEFAULT '0',
+ `qc_namespace` int(11) NOT NULL DEFAULT '0',
+ `qc_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ KEY `qc_type` (`qc_type`,`qc_value`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_querycache`
+--
+
+LOCK TABLES `mw_querycache` WRITE;
+/*!40000 ALTER TABLE `mw_querycache` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_querycache` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_querycache_info`
+--
+
+DROP TABLE IF EXISTS `mw_querycache_info`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_querycache_info` (
+ `qci_type` varbinary(32) NOT NULL DEFAULT '',
+ `qci_timestamp` binary(14) NOT NULL DEFAULT '19700101000000',
+ UNIQUE KEY `qci_type` (`qci_type`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_querycache_info`
+--
+
+LOCK TABLES `mw_querycache_info` WRITE;
+/*!40000 ALTER TABLE `mw_querycache_info` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_querycache_info` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_querycachetwo`
+--
+
+DROP TABLE IF EXISTS `mw_querycachetwo`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_querycachetwo` (
+ `qcc_type` varbinary(32) NOT NULL,
+ `qcc_value` int(10) unsigned NOT NULL DEFAULT '0',
+ `qcc_namespace` int(11) NOT NULL DEFAULT '0',
+ `qcc_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `qcc_namespacetwo` int(11) NOT NULL DEFAULT '0',
+ `qcc_titletwo` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ KEY `qcc_type` (`qcc_type`,`qcc_value`),
+ KEY `qcc_title` (`qcc_type`,`qcc_namespace`,`qcc_title`),
+ KEY `qcc_titletwo` (`qcc_type`,`qcc_namespacetwo`,`qcc_titletwo`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_querycachetwo`
+--
+
+LOCK TABLES `mw_querycachetwo` WRITE;
+/*!40000 ALTER TABLE `mw_querycachetwo` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_querycachetwo` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_recentchanges`
+--
+
+DROP TABLE IF EXISTS `mw_recentchanges`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_recentchanges` (
+ `rc_id` int(11) NOT NULL AUTO_INCREMENT,
+ `rc_timestamp` varbinary(14) NOT NULL DEFAULT '',
+ `rc_cur_time` varbinary(14) NOT NULL DEFAULT '',
+ `rc_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `rc_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `rc_namespace` int(11) NOT NULL DEFAULT '0',
+ `rc_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `rc_comment` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `rc_minor` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_bot` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_new` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_cur_id` int(10) unsigned NOT NULL DEFAULT '0',
+ `rc_this_oldid` int(10) unsigned NOT NULL DEFAULT '0',
+ `rc_last_oldid` int(10) unsigned NOT NULL DEFAULT '0',
+ `rc_type` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_moved_to_ns` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_moved_to_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `rc_patrolled` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_ip` varbinary(40) NOT NULL DEFAULT '',
+ `rc_old_len` int(11) DEFAULT NULL,
+ `rc_new_len` int(11) DEFAULT NULL,
+ `rc_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_logid` int(10) unsigned NOT NULL DEFAULT '0',
+ `rc_log_type` varbinary(255) DEFAULT NULL,
+ `rc_log_action` varbinary(255) DEFAULT NULL,
+ `rc_params` blob,
+ PRIMARY KEY (`rc_id`),
+ KEY `rc_timestamp` (`rc_timestamp`),
+ KEY `rc_namespace_title` (`rc_namespace`,`rc_title`),
+ KEY `rc_cur_id` (`rc_cur_id`),
+ KEY `new_name_timestamp` (`rc_new`,`rc_namespace`,`rc_timestamp`),
+ KEY `rc_ip` (`rc_ip`),
+ KEY `rc_ns_usertext` (`rc_namespace`,`rc_user_text`),
+ KEY `rc_user_text` (`rc_user_text`,`rc_timestamp`)
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_recentchanges`
+--
+
+LOCK TABLES `mw_recentchanges` WRITE;
+/*!40000 ALTER TABLE `mw_recentchanges` DISABLE KEYS */;
+INSERT INTO `mw_recentchanges` VALUES (1,'20110107184113','20110107184113',0,'MediaWiki Default',0,'Main_Page','',0,0,1,1,1,0,1,0,'',0,'::1',0,438,0,0,NULL,'',''),(2,'20110110173131','20110110173131',1,'WikiSysop',0,'TestResources','Created page with \"Test the the SimpleSelenium database was loaded correctly\"',0,0,1,2,2,0,1,0,'',1,'::1',0,57,0,0,NULL,'','');
+/*!40000 ALTER TABLE `mw_recentchanges` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_redirect`
+--
+
+DROP TABLE IF EXISTS `mw_redirect`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_redirect` (
+ `rd_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `rd_namespace` int(11) NOT NULL DEFAULT '0',
+ `rd_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `rd_interwiki` varchar(32) DEFAULT NULL,
+ `rd_fragment` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
+ PRIMARY KEY (`rd_from`),
+ KEY `rd_ns_title` (`rd_namespace`,`rd_title`,`rd_from`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_redirect`
+--
+
+LOCK TABLES `mw_redirect` WRITE;
+/*!40000 ALTER TABLE `mw_redirect` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_redirect` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_revision`
+--
+
+DROP TABLE IF EXISTS `mw_revision`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_revision` (
+ `rev_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `rev_page` int(10) unsigned NOT NULL,
+ `rev_text_id` int(10) unsigned NOT NULL,
+ `rev_comment` tinyblob NOT NULL,
+ `rev_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `rev_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `rev_timestamp` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `rev_minor_edit` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rev_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rev_len` int(10) unsigned DEFAULT NULL,
+ `rev_parent_id` int(10) unsigned DEFAULT NULL,
+ PRIMARY KEY (`rev_id`),
+ UNIQUE KEY `rev_page_id` (`rev_page`,`rev_id`),
+ KEY `rev_timestamp` (`rev_timestamp`),
+ KEY `page_timestamp` (`rev_page`,`rev_timestamp`),
+ KEY `user_timestamp` (`rev_user`,`rev_timestamp`),
+ KEY `usertext_timestamp` (`rev_user_text`,`rev_timestamp`)
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1 MAX_ROWS=10000000 AVG_ROW_LENGTH=1024;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_revision`
+--
+
+LOCK TABLES `mw_revision` WRITE;
+/*!40000 ALTER TABLE `mw_revision` DISABLE KEYS */;
+INSERT INTO `mw_revision` VALUES (1,1,1,'',0,'MediaWiki Default','20110107184113',0,0,438,0),(2,2,2,'Created page with \"Test the the SimpleSelenium database was loaded correctly\"',1,'WikiSysop','20110110173131',0,0,57,0);
+/*!40000 ALTER TABLE `mw_revision` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_searchindex`
+--
+
+DROP TABLE IF EXISTS `mw_searchindex`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_searchindex` (
+ `si_page` int(10) unsigned NOT NULL,
+ `si_title` varchar(255) NOT NULL DEFAULT '',
+ `si_text` mediumtext NOT NULL,
+ UNIQUE KEY `si_page` (`si_page`),
+ FULLTEXT KEY `si_title` (`si_title`),
+ FULLTEXT KEY `si_text` (`si_text`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_searchindex`
+--
+
+LOCK TABLES `mw_searchindex` WRITE;
+/*!40000 ALTER TABLE `mw_searchindex` DISABLE KEYS */;
+INSERT INTO `mw_searchindex` VALUES (1,'main page',' mediawiki hasu800 been successfully installed. consult theu800 user user\'su800 guide foru800 information onu800 using theu800 wiki software. getting started getting started getting started configuration settings list mediawiki faqu800 mediawiki release mailing list '),(2,'testresources',' test theu800 theu800 simpleselenium database wasu800 loaded correctly ');
+/*!40000 ALTER TABLE `mw_searchindex` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_site_stats`
+--
+
+DROP TABLE IF EXISTS `mw_site_stats`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_site_stats` (
+ `ss_row_id` int(10) unsigned NOT NULL,
+ `ss_total_views` bigint(20) unsigned DEFAULT '0',
+ `ss_total_edits` bigint(20) unsigned DEFAULT '0',
+ `ss_good_articles` bigint(20) unsigned DEFAULT '0',
+ `ss_total_pages` bigint(20) DEFAULT '-1',
+ `ss_users` bigint(20) DEFAULT '-1',
+ `ss_active_users` bigint(20) DEFAULT '-1',
+ `ss_admins` int(11) DEFAULT '-1',
+ `ss_images` int(11) DEFAULT '0',
+ UNIQUE KEY `ss_row_id` (`ss_row_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_site_stats`
+--
+
+LOCK TABLES `mw_site_stats` WRITE;
+/*!40000 ALTER TABLE `mw_site_stats` DISABLE KEYS */;
+INSERT INTO `mw_site_stats` VALUES (1,3,2,1,2,1,-1,-1,0);
+/*!40000 ALTER TABLE `mw_site_stats` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_tag_summary`
+--
+
+DROP TABLE IF EXISTS `mw_tag_summary`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_tag_summary` (
+ `ts_rc_id` int(11) DEFAULT NULL,
+ `ts_log_id` int(11) DEFAULT NULL,
+ `ts_rev_id` int(11) DEFAULT NULL,
+ `ts_tags` blob NOT NULL,
+ UNIQUE KEY `tag_summary_rc_id` (`ts_rc_id`),
+ UNIQUE KEY `tag_summary_log_id` (`ts_log_id`),
+ UNIQUE KEY `tag_summary_rev_id` (`ts_rev_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_tag_summary`
+--
+
+LOCK TABLES `mw_tag_summary` WRITE;
+/*!40000 ALTER TABLE `mw_tag_summary` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_tag_summary` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_templatelinks`
+--
+
+DROP TABLE IF EXISTS `mw_templatelinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_templatelinks` (
+ `tl_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `tl_namespace` int(11) NOT NULL DEFAULT '0',
+ `tl_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ UNIQUE KEY `tl_from` (`tl_from`,`tl_namespace`,`tl_title`),
+ UNIQUE KEY `tl_namespace` (`tl_namespace`,`tl_title`,`tl_from`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_templatelinks`
+--
+
+LOCK TABLES `mw_templatelinks` WRITE;
+/*!40000 ALTER TABLE `mw_templatelinks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_templatelinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_text`
+--
+
+DROP TABLE IF EXISTS `mw_text`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_text` (
+ `old_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `old_text` mediumblob NOT NULL,
+ `old_flags` tinyblob NOT NULL,
+ PRIMARY KEY (`old_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1 MAX_ROWS=10000000 AVG_ROW_LENGTH=10240;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_text`
+--
+
+LOCK TABLES `mw_text` WRITE;
+/*!40000 ALTER TABLE `mw_text` DISABLE KEYS */;
+INSERT INTO `mw_text` VALUES (1,'\'\'\'MediaWiki has been successfully installed.\'\'\'\n\nConsult the [http://meta.wikimedia.org/wiki/Help:Contents User\'s Guide] for information on using the wiki software.\n\n== Getting started ==\n* [http://www.mediawiki.org/wiki/Manual:Configuration_settings Configuration settings list]\n* [http://www.mediawiki.org/wiki/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list]','utf-8'),(2,'Test the the SimpleSelenium database was loaded correctly','utf-8');
+/*!40000 ALTER TABLE `mw_text` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_trackbacks`
+--
+
+DROP TABLE IF EXISTS `mw_trackbacks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_trackbacks` (
+ `tb_id` int(11) NOT NULL AUTO_INCREMENT,
+ `tb_page` int(11) DEFAULT NULL,
+ `tb_title` varchar(255) NOT NULL,
+ `tb_url` blob NOT NULL,
+ `tb_ex` text,
+ `tb_name` varchar(255) DEFAULT NULL,
+ PRIMARY KEY (`tb_id`),
+ KEY `tb_page` (`tb_page`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_trackbacks`
+--
+
+LOCK TABLES `mw_trackbacks` WRITE;
+/*!40000 ALTER TABLE `mw_trackbacks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_trackbacks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_transcache`
+--
+
+DROP TABLE IF EXISTS `mw_transcache`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_transcache` (
+ `tc_url` varbinary(255) NOT NULL,
+ `tc_contents` text,
+ `tc_time` binary(14) DEFAULT NULL,
+ UNIQUE KEY `tc_url_idx` (`tc_url`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_transcache`
+--
+
+LOCK TABLES `mw_transcache` WRITE;
+/*!40000 ALTER TABLE `mw_transcache` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_transcache` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_updatelog`
+--
+
+DROP TABLE IF EXISTS `mw_updatelog`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_updatelog` (
+ `ul_key` varchar(255) NOT NULL,
+ `ul_value` blob,
+ PRIMARY KEY (`ul_key`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_updatelog`
+--
+
+LOCK TABLES `mw_updatelog` WRITE;
+/*!40000 ALTER TABLE `mw_updatelog` DISABLE KEYS */;
+INSERT INTO `mw_updatelog` VALUES ('cl_fields_update',NULL),('convert transcache field',NULL),('mime_minor_length',NULL),('populate category',NULL),('populate rev_len',NULL),('populate rev_parent_id',NULL),('updatelist-1.18alpha-1294425799','a:128:{i:0;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"ipblocks\";i:2;s:6:\"ipb_id\";i:3;s:18:\"patch-ipblocks.sql\";}i:1;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"ipblocks\";i:2;s:10:\"ipb_expiry\";i:3;s:20:\"patch-ipb_expiry.sql\";}i:2;a:1:{i:0;s:17:\"doInterwikiUpdate\";}i:3;a:1:{i:0;s:13:\"doIndexUpdate\";}i:4;a:3:{i:0;s:8:\"addTable\";i:1;s:10:\"hitcounter\";i:2;s:20:\"patch-hitcounter.sql\";}i:5;a:4:{i:0;s:8:\"addField\";i:1;s:13:\"recentchanges\";i:2;s:7:\"rc_type\";i:3;s:17:\"patch-rc_type.sql\";}i:6;a:4:{i:0;s:8:\"addField\";i:1;s:4:\"user\";i:2;s:14:\"user_real_name\";i:3;s:23:\"patch-user-realname.sql\";}i:7;a:3:{i:0;s:8:\"addTable\";i:1;s:10:\"querycache\";i:2;s:20:\"patch-querycache.sql\";}i:8;a:3:{i:0;s:8:\"addTable\";i:1;s:11:\"objectcache\";i:2;s:21:\"patch-objectcache.sql\";}i:9;a:3:{i:0;s:8:\"addTable\";i:1;s:13:\"categorylinks\";i:2;s:23:\"patch-categorylinks.sql\";}i:10;a:1:{i:0;s:16:\"doOldLinksUpdate\";}i:11;a:1:{i:0;s:22:\"doFixAncientImagelinks\";}i:12;a:4:{i:0;s:8:\"addField\";i:1;s:13:\"recentchanges\";i:2;s:5:\"rc_ip\";i:3;s:15:\"patch-rc_ip.sql\";}i:13;a:4:{i:0;s:8:\"addIndex\";i:1;s:5:\"image\";i:2;s:7:\"PRIMARY\";i:3;s:28:\"patch-image_name_primary.sql\";}i:14;a:4:{i:0;s:8:\"addField\";i:1;s:13:\"recentchanges\";i:2;s:5:\"rc_id\";i:3;s:15:\"patch-rc_id.sql\";}i:15;a:4:{i:0;s:8:\"addField\";i:1;s:13:\"recentchanges\";i:2;s:12:\"rc_patrolled\";i:3;s:19:\"patch-rc-patrol.sql\";}i:16;a:3:{i:0;s:8:\"addTable\";i:1;s:7:\"logging\";i:2;s:17:\"patch-logging.sql\";}i:17;a:4:{i:0;s:8:\"addField\";i:1;s:4:\"user\";i:2;s:10:\"user_token\";i:3;s:20:\"patch-user_token.sql\";}i:18;a:4:{i:0;s:8:\"addField\";i:1;s:9:\"watchlist\";i:2;s:24:\"wl_notificationtimestamp\";i:3;s:28:\"patch-email-notification.sql\";}i:19;a:1:{i:0;s:17:\"doWatchlistUpdate\";}i:20;a:4:{i:0;s:9:\"dropField\";i:1;s:4:\"user\";i:2;s:33:\"user_emailauthenticationtimestamp\";i:3;s:30:\"patch-email-authentication.sql\";}i:21;a:1:{i:0;s:21:\"doSchemaRestructuring\";}i:22;a:4:{i:0;s:8:\"addField\";i:1;s:7:\"logging\";i:2;s:10:\"log_params\";i:3;s:20:\"patch-log_params.sql\";}i:23;a:4:{i:0;s:8:\"checkBin\";i:1;s:7:\"logging\";i:2;s:9:\"log_title\";i:3;s:23:\"patch-logging-title.sql\";}i:24;a:4:{i:0;s:8:\"addField\";i:1;s:7:\"archive\";i:2;s:9:\"ar_rev_id\";i:3;s:24:\"patch-archive-rev_id.sql\";}i:25;a:4:{i:0;s:8:\"addField\";i:1;s:4:\"page\";i:2;s:8:\"page_len\";i:3;s:18:\"patch-page_len.sql\";}i:26;a:4:{i:0;s:9:\"dropField\";i:1;s:8:\"revision\";i:2;s:17:\"inverse_timestamp\";i:3;s:27:\"patch-inverse_timestamp.sql\";}i:27;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"revision\";i:2;s:11:\"rev_text_id\";i:3;s:21:\"patch-rev_text_id.sql\";}i:28;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"revision\";i:2;s:11:\"rev_deleted\";i:3;s:21:\"patch-rev_deleted.sql\";}i:29;a:4:{i:0;s:8:\"addField\";i:1;s:5:\"image\";i:2;s:9:\"img_width\";i:3;s:19:\"patch-img_width.sql\";}i:30;a:4:{i:0;s:8:\"addField\";i:1;s:5:\"image\";i:2;s:12:\"img_metadata\";i:3;s:22:\"patch-img_metadata.sql\";}i:31;a:4:{i:0;s:8:\"addField\";i:1;s:4:\"user\";i:2;s:16:\"user_email_token\";i:3;s:26:\"patch-user_email_token.sql\";}i:32;a:4:{i:0;s:8:\"addField\";i:1;s:7:\"archive\";i:2;s:10:\"ar_text_id\";i:3;s:25:\"patch-archive-text_id.sql\";}i:33;a:1:{i:0;s:15:\"doNamespaceSize\";}i:34;a:4:{i:0;s:8:\"addField\";i:1;s:5:\"image\";i:2;s:14:\"img_media_type\";i:3;s:24:\"patch-img_media_type.sql\";}i:35;a:1:{i:0;s:17:\"doPagelinksUpdate\";}i:36;a:4:{i:0;s:9:\"dropField\";i:1;s:5:\"image\";i:2;s:8:\"img_type\";i:3;s:23:\"patch-drop_img_type.sql\";}i:37;a:1:{i:0;s:18:\"doUserUniqueUpdate\";}i:38;a:1:{i:0;s:18:\"doUserGroupsUpdate\";}i:39;a:4:{i:0;s:8:\"addField\";i:1;s:10:\"site_stats\";i:2;s:14:\"ss_total_pages\";i:3;s:27:\"patch-ss_total_articles.sql\";}i:40;a:3:{i:0;s:8:\"addTable\";i:1;s:12:\"user_newtalk\";i:2;s:22:\"patch-usernewtalk2.sql\";}i:41;a:3:{i:0;s:8:\"addTable\";i:1;s:10:\"transcache\";i:2;s:20:\"patch-transcache.sql\";}i:42;a:4:{i:0;s:8:\"addField\";i:1;s:9:\"interwiki\";i:2;s:8:\"iw_trans\";i:3;s:25:\"patch-interwiki-trans.sql\";}i:43;a:3:{i:0;s:8:\"addTable\";i:1;s:10:\"trackbacks\";i:2;s:20:\"patch-trackbacks.sql\";}i:44;a:1:{i:0;s:15:\"doWatchlistNull\";}i:45;a:4:{i:0;s:8:\"addIndex\";i:1;s:7:\"logging\";i:2;s:5:\"times\";i:3;s:29:\"patch-logging-times-index.sql\";}i:46;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"ipblocks\";i:2;s:15:\"ipb_range_start\";i:3;s:25:\"patch-ipb_range_start.sql\";}i:47;a:1:{i:0;s:18:\"doPageRandomUpdate\";}i:48;a:4:{i:0;s:8:\"addField\";i:1;s:4:\"user\";i:2;s:17:\"user_registration\";i:3;s:27:\"patch-user_registration.sql\";}i:49;a:1:{i:0;s:21:\"doTemplatelinksUpdate\";}i:50;a:3:{i:0;s:8:\"addTable\";i:1;s:13:\"externallinks\";i:2;s:23:\"patch-externallinks.sql\";}i:51;a:3:{i:0;s:8:\"addTable\";i:1;s:3:\"job\";i:2;s:13:\"patch-job.sql\";}i:52;a:4:{i:0;s:8:\"addField\";i:1;s:10:\"site_stats\";i:2;s:9:\"ss_images\";i:3;s:19:\"patch-ss_images.sql\";}i:53;a:3:{i:0;s:8:\"addTable\";i:1;s:9:\"langlinks\";i:2;s:19:\"patch-langlinks.sql\";}i:54;a:3:{i:0;s:8:\"addTable\";i:1;s:15:\"querycache_info\";i:2;s:24:\"patch-querycacheinfo.sql\";}i:55;a:3:{i:0;s:8:\"addTable\";i:1;s:11:\"filearchive\";i:2;s:21:\"patch-filearchive.sql\";}i:56;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"ipblocks\";i:2;s:13:\"ipb_anon_only\";i:3;s:23:\"patch-ipb_anon_only.sql\";}i:57;a:4:{i:0;s:8:\"addIndex\";i:1;s:13:\"recentchanges\";i:2;s:14:\"rc_ns_usertext\";i:3;s:31:\"patch-recentchanges-utindex.sql\";}i:58;a:4:{i:0;s:8:\"addIndex\";i:1;s:13:\"recentchanges\";i:2;s:12:\"rc_user_text\";i:3;s:28:\"patch-rc_user_text-index.sql\";}i:59;a:4:{i:0;s:8:\"addField\";i:1;s:4:\"user\";i:2;s:17:\"user_newpass_time\";i:3;s:27:\"patch-user_newpass_time.sql\";}i:60;a:3:{i:0;s:8:\"addTable\";i:1;s:8:\"redirect\";i:2;s:18:\"patch-redirect.sql\";}i:61;a:3:{i:0;s:8:\"addTable\";i:1;s:13:\"querycachetwo\";i:2;s:23:\"patch-querycachetwo.sql\";}i:62;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"ipblocks\";i:2;s:20:\"ipb_enable_autoblock\";i:3;s:32:\"patch-ipb_optional_autoblock.sql\";}i:63;a:1:{i:0;s:26:\"doBacklinkingIndicesUpdate\";}i:64;a:4:{i:0;s:8:\"addField\";i:1;s:13:\"recentchanges\";i:2;s:10:\"rc_old_len\";i:3;s:16:\"patch-rc_len.sql\";}i:65;a:4:{i:0;s:8:\"addField\";i:1;s:4:\"user\";i:2;s:14:\"user_editcount\";i:3;s:24:\"patch-user_editcount.sql\";}i:66;a:1:{i:0;s:20:\"doRestrictionsUpdate\";}i:67;a:4:{i:0;s:8:\"addField\";i:1;s:7:\"logging\";i:2;s:6:\"log_id\";i:3;s:16:\"patch-log_id.sql\";}i:68;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"revision\";i:2;s:13:\"rev_parent_id\";i:3;s:23:\"patch-rev_parent_id.sql\";}i:69;a:4:{i:0;s:8:\"addField\";i:1;s:17:\"page_restrictions\";i:2;s:5:\"pr_id\";i:3;s:35:\"patch-page_restrictions_sortkey.sql\";}i:70;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"revision\";i:2;s:7:\"rev_len\";i:3;s:17:\"patch-rev_len.sql\";}i:71;a:4:{i:0;s:8:\"addField\";i:1;s:13:\"recentchanges\";i:2;s:10:\"rc_deleted\";i:3;s:20:\"patch-rc_deleted.sql\";}i:72;a:4:{i:0;s:8:\"addField\";i:1;s:7:\"logging\";i:2;s:11:\"log_deleted\";i:3;s:21:\"patch-log_deleted.sql\";}i:73;a:4:{i:0;s:8:\"addField\";i:1;s:7:\"archive\";i:2;s:10:\"ar_deleted\";i:3;s:20:\"patch-ar_deleted.sql\";}i:74;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"ipblocks\";i:2;s:11:\"ipb_deleted\";i:3;s:21:\"patch-ipb_deleted.sql\";}i:75;a:4:{i:0;s:8:\"addField\";i:1;s:11:\"filearchive\";i:2;s:10:\"fa_deleted\";i:3;s:20:\"patch-fa_deleted.sql\";}i:76;a:4:{i:0;s:8:\"addField\";i:1;s:7:\"archive\";i:2;s:6:\"ar_len\";i:3;s:16:\"patch-ar_len.sql\";}i:77;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"ipblocks\";i:2;s:15:\"ipb_block_email\";i:3;s:22:\"patch-ipb_emailban.sql\";}i:78;a:1:{i:0;s:28:\"doCategorylinksIndicesUpdate\";}i:79;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"oldimage\";i:2;s:11:\"oi_metadata\";i:3;s:21:\"patch-oi_metadata.sql\";}i:80;a:4:{i:0;s:8:\"addIndex\";i:1;s:7:\"archive\";i:2;s:18:\"usertext_timestamp\";i:3;s:28:\"patch-archive-user-index.sql\";}i:81;a:4:{i:0;s:8:\"addIndex\";i:1;s:5:\"image\";i:2;s:22:\"img_usertext_timestamp\";i:3;s:26:\"patch-image-user-index.sql\";}i:82;a:4:{i:0;s:8:\"addIndex\";i:1;s:8:\"oldimage\";i:2;s:21:\"oi_usertext_timestamp\";i:3;s:29:\"patch-oldimage-user-index.sql\";}i:83;a:4:{i:0;s:8:\"addField\";i:1;s:7:\"archive\";i:2;s:10:\"ar_page_id\";i:3;s:25:\"patch-archive-page_id.sql\";}i:84;a:4:{i:0;s:8:\"addField\";i:1;s:5:\"image\";i:2;s:8:\"img_sha1\";i:3;s:18:\"patch-img_sha1.sql\";}i:85;a:3:{i:0;s:8:\"addTable\";i:1;s:16:\"protected_titles\";i:2;s:26:\"patch-protected_titles.sql\";}i:86;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"ipblocks\";i:2;s:11:\"ipb_by_text\";i:3;s:21:\"patch-ipb_by_text.sql\";}i:87;a:3:{i:0;s:8:\"addTable\";i:1;s:10:\"page_props\";i:2;s:20:\"patch-page_props.sql\";}i:88;a:3:{i:0;s:8:\"addTable\";i:1;s:9:\"updatelog\";i:2;s:19:\"patch-updatelog.sql\";}i:89;a:3:{i:0;s:8:\"addTable\";i:1;s:8:\"category\";i:2;s:18:\"patch-category.sql\";}i:90;a:1:{i:0;s:20:\"doCategoryPopulation\";}i:91;a:4:{i:0;s:8:\"addField\";i:1;s:7:\"archive\";i:2;s:12:\"ar_parent_id\";i:3;s:22:\"patch-ar_parent_id.sql\";}i:92;a:4:{i:0;s:8:\"addField\";i:1;s:12:\"user_newtalk\";i:2;s:19:\"user_last_timestamp\";i:3;s:29:\"patch-user_last_timestamp.sql\";}i:93;a:1:{i:0;s:18:\"doPopulateParentId\";}i:94;a:4:{i:0;s:8:\"checkBin\";i:1;s:16:\"protected_titles\";i:2;s:8:\"pt_title\";i:3;s:27:\"patch-pt_title-encoding.sql\";}i:95;a:1:{i:0;s:28:\"doMaybeProfilingMemoryUpdate\";}i:96;a:1:{i:0;s:26:\"doFilearchiveIndicesUpdate\";}i:97;a:4:{i:0;s:8:\"addField\";i:1;s:10:\"site_stats\";i:2;s:15:\"ss_active_users\";i:3;s:25:\"patch-ss_active_users.sql\";}i:98;a:1:{i:0;s:17:\"doActiveUsersInit\";}i:99;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"ipblocks\";i:2;s:18:\"ipb_allow_usertalk\";i:3;s:28:\"patch-ipb_allow_usertalk.sql\";}i:100;a:1:{i:0;s:14:\"doUniquePlTlIl\";}i:101;a:3:{i:0;s:8:\"addTable\";i:1;s:10:\"change_tag\";i:2;s:20:\"patch-change_tag.sql\";}i:102;a:3:{i:0;s:8:\"addTable\";i:1;s:11:\"tag_summary\";i:2;s:20:\"patch-change_tag.sql\";}i:103;a:3:{i:0;s:8:\"addTable\";i:1;s:9:\"valid_tag\";i:2;s:20:\"patch-change_tag.sql\";}i:104;a:3:{i:0;s:8:\"addTable\";i:1;s:15:\"user_properties\";i:2;s:25:\"patch-user_properties.sql\";}i:105;a:3:{i:0;s:8:\"addTable\";i:1;s:10:\"log_search\";i:2;s:20:\"patch-log_search.sql\";}i:106;a:1:{i:0;s:21:\"doLogSearchPopulation\";}i:107;a:4:{i:0;s:8:\"addField\";i:1;s:7:\"logging\";i:2;s:13:\"log_user_text\";i:3;s:23:\"patch-log_user_text.sql\";}i:108;a:3:{i:0;s:8:\"addTable\";i:1;s:10:\"l10n_cache\";i:2;s:20:\"patch-l10n_cache.sql\";}i:109;a:3:{i:0;s:8:\"addTable\";i:1;s:13:\"external_user\";i:2;s:23:\"patch-external_user.sql\";}i:110;a:4:{i:0;s:8:\"addIndex\";i:1;s:10:\"log_search\";i:2;s:12:\"ls_field_val\";i:3;s:33:\"patch-log_search-rename-index.sql\";}i:111;a:4:{i:0;s:8:\"addIndex\";i:1;s:10:\"change_tag\";i:2;s:17:\"change_tag_rc_tag\";i:3;s:28:\"patch-change_tag-indexes.sql\";}i:112;a:4:{i:0;s:8:\"addField\";i:1;s:8:\"redirect\";i:2;s:12:\"rd_interwiki\";i:3;s:22:\"patch-rd_interwiki.sql\";}i:113;a:1:{i:0;s:23:\"doUpdateTranscacheField\";}i:114;a:1:{i:0;s:14:\"renameEuWikiId\";}i:115;a:1:{i:0;s:22:\"doUpdateMimeMinorField\";}i:116;a:1:{i:0;s:16:\"doPopulateRevLen\";}i:117;a:3:{i:0;s:8:\"addTable\";i:1;s:7:\"iwlinks\";i:2;s:17:\"patch-iwlinks.sql\";}i:118;a:4:{i:0;s:8:\"addIndex\";i:1;s:7:\"iwlinks\";i:2;s:21:\"iwl_prefix_title_from\";i:3;s:27:\"patch-rename-iwl_prefix.sql\";}i:119;a:4:{i:0;s:8:\"addField\";i:1;s:9:\"updatelog\";i:2;s:8:\"ul_value\";i:3;s:18:\"patch-ul_value.sql\";}i:120;a:4:{i:0;s:8:\"addField\";i:1;s:9:\"interwiki\";i:2;s:6:\"iw_api\";i:3;s:27:\"patch-iw_api_and_wikiid.sql\";}i:121;a:4:{i:0;s:9:\"dropIndex\";i:1;s:7:\"iwlinks\";i:2;s:10:\"iwl_prefix\";i:3;s:25:\"patch-kill-iwl_prefix.sql\";}i:122;a:4:{i:0;s:9:\"dropIndex\";i:1;s:7:\"iwlinks\";i:2;s:21:\"iwl_prefix_from_title\";i:3;s:22:\"patch-kill-iwl_pft.sql\";}i:123;a:4:{i:0;s:8:\"addField\";i:1;s:13:\"categorylinks\";i:2;s:12:\"cl_collation\";i:3;s:40:\"patch-categorylinks-better-collation.sql\";}i:124;a:1:{i:0;s:16:\"doClFieldsUpdate\";}i:125;a:1:{i:0;s:17:\"doCollationUpdate\";}i:126;a:3:{i:0;s:8:\"addTable\";i:1;s:12:\"msg_resource\";i:2;s:22:\"patch-msg_resource.sql\";}i:127;a:3:{i:0;s:8:\"addTable\";i:1;s:11:\"module_deps\";i:2;s:21:\"patch-module_deps.sql\";}}');
+/*!40000 ALTER TABLE `mw_updatelog` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_user`
+--
+
+DROP TABLE IF EXISTS `mw_user`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_user` (
+ `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `user_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `user_real_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `user_password` tinyblob NOT NULL,
+ `user_newpassword` tinyblob NOT NULL,
+ `user_newpass_time` binary(14) DEFAULT NULL,
+ `user_email` tinytext NOT NULL,
+ `user_options` blob NOT NULL,
+ `user_touched` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `user_token` binary(32) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `user_email_authenticated` binary(14) DEFAULT NULL,
+ `user_email_token` binary(32) DEFAULT NULL,
+ `user_email_token_expires` binary(14) DEFAULT NULL,
+ `user_registration` binary(14) DEFAULT NULL,
+ `user_editcount` int(11) DEFAULT NULL,
+ PRIMARY KEY (`user_id`),
+ UNIQUE KEY `user_name` (`user_name`),
+ KEY `user_email_token` (`user_email_token`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_user`
+--
+
+LOCK TABLES `mw_user` WRITE;
+/*!40000 ALTER TABLE `mw_user` DISABLE KEYS */;
+INSERT INTO `mw_user` VALUES (1,'WikiSysop','',':B:9c595470:df2c1237ae75896744457e7dfbeb7f90','',NULL,'','','20110110173136','5e3b582786fa8150118cfa78f18de0c5',NULL,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',NULL,'20110107184113',1);
+/*!40000 ALTER TABLE `mw_user` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_user_groups`
+--
+
+DROP TABLE IF EXISTS `mw_user_groups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_user_groups` (
+ `ug_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `ug_group` varbinary(16) NOT NULL DEFAULT '',
+ UNIQUE KEY `ug_user_group` (`ug_user`,`ug_group`),
+ KEY `ug_group` (`ug_group`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_user_groups`
+--
+
+LOCK TABLES `mw_user_groups` WRITE;
+/*!40000 ALTER TABLE `mw_user_groups` DISABLE KEYS */;
+INSERT INTO `mw_user_groups` VALUES (1,'bureaucrat'),(1,'sysop');
+/*!40000 ALTER TABLE `mw_user_groups` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_user_newtalk`
+--
+
+DROP TABLE IF EXISTS `mw_user_newtalk`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_user_newtalk` (
+ `user_id` int(11) NOT NULL DEFAULT '0',
+ `user_ip` varbinary(40) NOT NULL DEFAULT '',
+ `user_last_timestamp` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ KEY `user_id` (`user_id`),
+ KEY `user_ip` (`user_ip`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_user_newtalk`
+--
+
+LOCK TABLES `mw_user_newtalk` WRITE;
+/*!40000 ALTER TABLE `mw_user_newtalk` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_user_newtalk` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_user_properties`
+--
+
+DROP TABLE IF EXISTS `mw_user_properties`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_user_properties` (
+ `up_user` int(11) NOT NULL,
+ `up_property` varbinary(32) NOT NULL,
+ `up_value` blob,
+ UNIQUE KEY `user_properties_user_property` (`up_user`,`up_property`),
+ KEY `user_properties_property` (`up_property`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_user_properties`
+--
+
+LOCK TABLES `mw_user_properties` WRITE;
+/*!40000 ALTER TABLE `mw_user_properties` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_user_properties` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_valid_tag`
+--
+
+DROP TABLE IF EXISTS `mw_valid_tag`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_valid_tag` (
+ `vt_tag` varchar(255) NOT NULL,
+ PRIMARY KEY (`vt_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_valid_tag`
+--
+
+LOCK TABLES `mw_valid_tag` WRITE;
+/*!40000 ALTER TABLE `mw_valid_tag` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_valid_tag` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_watchlist`
+--
+
+DROP TABLE IF EXISTS `mw_watchlist`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_watchlist` (
+ `wl_user` int(10) unsigned NOT NULL,
+ `wl_namespace` int(11) NOT NULL DEFAULT '0',
+ `wl_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `wl_notificationtimestamp` varbinary(14) DEFAULT NULL,
+ UNIQUE KEY `wl_user` (`wl_user`,`wl_namespace`,`wl_title`),
+ KEY `namespace_title` (`wl_namespace`,`wl_title`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_watchlist`
+--
+
+LOCK TABLES `mw_watchlist` WRITE;
+/*!40000 ALTER TABLE `mw_watchlist` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_watchlist` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2011-01-10 9:34:34
diff --git a/tests/selenium/data/SimpleSeleniumTestImages.zip b/tests/selenium/data/SimpleSeleniumTestImages.zip
new file mode 100644
index 00000000..0374a1fb
--- /dev/null
+++ b/tests/selenium/data/SimpleSeleniumTestImages.zip
Binary files differ
diff --git a/tests/selenium/data/Wikipedia-logo-v2-de.png b/tests/selenium/data/Wikipedia-logo-v2-de.png
new file mode 100644
index 00000000..70385243
--- /dev/null
+++ b/tests/selenium/data/Wikipedia-logo-v2-de.png
Binary files differ
diff --git a/tests/selenium/data/mediawiki118_fresh_installation.sql b/tests/selenium/data/mediawiki118_fresh_installation.sql
new file mode 100644
index 00000000..89bc3191
--- /dev/null
+++ b/tests/selenium/data/mediawiki118_fresh_installation.sql
@@ -0,0 +1,1544 @@
+-- MySQL dump 10.13 Distrib 5.1.41, for Win32 (ia32)
+--
+-- Host: localhost Database: test_wiki
+-- ------------------------------------------------------
+-- Server version 5.1.41
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `mw_archive`
+--
+
+DROP TABLE IF EXISTS `mw_archive`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_archive` (
+ `ar_namespace` int(11) NOT NULL DEFAULT '0',
+ `ar_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `ar_text` mediumblob NOT NULL,
+ `ar_comment` tinyblob NOT NULL,
+ `ar_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `ar_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `ar_timestamp` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `ar_minor_edit` tinyint(4) NOT NULL DEFAULT '0',
+ `ar_flags` tinyblob NOT NULL,
+ `ar_rev_id` int(10) unsigned DEFAULT NULL,
+ `ar_text_id` int(10) unsigned DEFAULT NULL,
+ `ar_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `ar_len` int(10) unsigned DEFAULT NULL,
+ `ar_page_id` int(10) unsigned DEFAULT NULL,
+ `ar_parent_id` int(10) unsigned DEFAULT NULL,
+ KEY `name_title_timestamp` (`ar_namespace`,`ar_title`,`ar_timestamp`),
+ KEY `usertext_timestamp` (`ar_user_text`,`ar_timestamp`),
+ KEY `ar_page_revid` (`ar_namespace`,`ar_title`,`ar_rev_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_archive`
+--
+
+LOCK TABLES `mw_archive` WRITE;
+/*!40000 ALTER TABLE `mw_archive` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_archive` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_category`
+--
+
+DROP TABLE IF EXISTS `mw_category`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_category` (
+ `cat_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `cat_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `cat_pages` int(11) NOT NULL DEFAULT '0',
+ `cat_subcats` int(11) NOT NULL DEFAULT '0',
+ `cat_files` int(11) NOT NULL DEFAULT '0',
+ `cat_hidden` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`cat_id`),
+ UNIQUE KEY `cat_title` (`cat_title`),
+ KEY `cat_pages` (`cat_pages`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_category`
+--
+
+LOCK TABLES `mw_category` WRITE;
+/*!40000 ALTER TABLE `mw_category` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_category` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_categorylinks`
+--
+
+DROP TABLE IF EXISTS `mw_categorylinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_categorylinks` (
+ `cl_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `cl_to` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `cl_sortkey` varbinary(230) NOT NULL DEFAULT '',
+ `cl_sortkey_prefix` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `cl_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `cl_collation` varbinary(32) NOT NULL DEFAULT '',
+ `cl_type` enum('page','subcat','file') NOT NULL DEFAULT 'page',
+ UNIQUE KEY `cl_from` (`cl_from`,`cl_to`),
+ KEY `cl_sortkey` (`cl_to`,`cl_type`,`cl_sortkey`,`cl_from`),
+ KEY `cl_timestamp` (`cl_to`,`cl_timestamp`),
+ KEY `cl_collation` (`cl_collation`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_categorylinks`
+--
+
+LOCK TABLES `mw_categorylinks` WRITE;
+/*!40000 ALTER TABLE `mw_categorylinks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_categorylinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_change_tag`
+--
+
+DROP TABLE IF EXISTS `mw_change_tag`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_change_tag` (
+ `ct_rc_id` int(11) DEFAULT NULL,
+ `ct_log_id` int(11) DEFAULT NULL,
+ `ct_rev_id` int(11) DEFAULT NULL,
+ `ct_tag` varchar(255) NOT NULL,
+ `ct_params` blob,
+ UNIQUE KEY `change_tag_rc_tag` (`ct_rc_id`,`ct_tag`),
+ UNIQUE KEY `change_tag_log_tag` (`ct_log_id`,`ct_tag`),
+ UNIQUE KEY `change_tag_rev_tag` (`ct_rev_id`,`ct_tag`),
+ KEY `change_tag_tag_id` (`ct_tag`,`ct_rc_id`,`ct_rev_id`,`ct_log_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_change_tag`
+--
+
+LOCK TABLES `mw_change_tag` WRITE;
+/*!40000 ALTER TABLE `mw_change_tag` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_change_tag` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_external_user`
+--
+
+DROP TABLE IF EXISTS `mw_external_user`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_external_user` (
+ `eu_local_id` int(10) unsigned NOT NULL,
+ `eu_external_id` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ PRIMARY KEY (`eu_local_id`),
+ UNIQUE KEY `eu_external_id` (`eu_external_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_external_user`
+--
+
+LOCK TABLES `mw_external_user` WRITE;
+/*!40000 ALTER TABLE `mw_external_user` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_external_user` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_externallinks`
+--
+
+DROP TABLE IF EXISTS `mw_externallinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_externallinks` (
+ `el_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `el_to` blob NOT NULL,
+ `el_index` blob NOT NULL,
+ KEY `el_from` (`el_from`,`el_to`(40)),
+ KEY `el_to` (`el_to`(60),`el_from`),
+ KEY `el_index` (`el_index`(60))
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_externallinks`
+--
+
+LOCK TABLES `mw_externallinks` WRITE;
+/*!40000 ALTER TABLE `mw_externallinks` DISABLE KEYS */;
+INSERT INTO `mw_externallinks` VALUES (1,'http://meta.wikimedia.org/wiki/Help:Contents','http://org.wikimedia.meta./wiki/Help:Contents');
+INSERT INTO `mw_externallinks` VALUES (1,'http://www.mediawiki.org/wiki/Manual:Configuration_settings','http://org.mediawiki.www./wiki/Manual:Configuration_settings');
+INSERT INTO `mw_externallinks` VALUES (1,'http://www.mediawiki.org/wiki/Manual:FAQ','http://org.mediawiki.www./wiki/Manual:FAQ');
+INSERT INTO `mw_externallinks` VALUES (1,'https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce','https://org.wikimedia.lists./mailman/listinfo/mediawiki-announce');
+/*!40000 ALTER TABLE `mw_externallinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_filearchive`
+--
+
+DROP TABLE IF EXISTS `mw_filearchive`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_filearchive` (
+ `fa_id` int(11) NOT NULL AUTO_INCREMENT,
+ `fa_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `fa_archive_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT '',
+ `fa_storage_group` varbinary(16) DEFAULT NULL,
+ `fa_storage_key` varbinary(64) DEFAULT '',
+ `fa_deleted_user` int(11) DEFAULT NULL,
+ `fa_deleted_timestamp` binary(14) DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `fa_deleted_reason` text,
+ `fa_size` int(10) unsigned DEFAULT '0',
+ `fa_width` int(11) DEFAULT '0',
+ `fa_height` int(11) DEFAULT '0',
+ `fa_metadata` mediumblob,
+ `fa_bits` int(11) DEFAULT '0',
+ `fa_media_type` enum('UNKNOWN','BITMAP','DRAWING','AUDIO','VIDEO','MULTIMEDIA','OFFICE','TEXT','EXECUTABLE','ARCHIVE') DEFAULT NULL,
+ `fa_major_mime` enum('unknown','application','audio','image','text','video','message','model','multipart') DEFAULT 'unknown',
+ `fa_minor_mime` varbinary(100) DEFAULT 'unknown',
+ `fa_description` tinyblob,
+ `fa_user` int(10) unsigned DEFAULT '0',
+ `fa_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
+ `fa_timestamp` binary(14) DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `fa_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`fa_id`),
+ KEY `fa_name` (`fa_name`,`fa_timestamp`),
+ KEY `fa_storage_group` (`fa_storage_group`,`fa_storage_key`),
+ KEY `fa_deleted_timestamp` (`fa_deleted_timestamp`),
+ KEY `fa_user_timestamp` (`fa_user_text`,`fa_timestamp`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_filearchive`
+--
+
+LOCK TABLES `mw_filearchive` WRITE;
+/*!40000 ALTER TABLE `mw_filearchive` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_filearchive` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_hitcounter`
+--
+
+DROP TABLE IF EXISTS `mw_hitcounter`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_hitcounter` (
+ `hc_id` int(10) unsigned NOT NULL
+) ENGINE=MEMORY DEFAULT CHARSET=latin1 MAX_ROWS=25000;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_hitcounter`
+--
+
+LOCK TABLES `mw_hitcounter` WRITE;
+/*!40000 ALTER TABLE `mw_hitcounter` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_hitcounter` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_image`
+--
+
+DROP TABLE IF EXISTS `mw_image`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_image` (
+ `img_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `img_size` int(10) unsigned NOT NULL DEFAULT '0',
+ `img_width` int(11) NOT NULL DEFAULT '0',
+ `img_height` int(11) NOT NULL DEFAULT '0',
+ `img_metadata` mediumblob NOT NULL,
+ `img_bits` int(11) NOT NULL DEFAULT '0',
+ `img_media_type` enum('UNKNOWN','BITMAP','DRAWING','AUDIO','VIDEO','MULTIMEDIA','OFFICE','TEXT','EXECUTABLE','ARCHIVE') DEFAULT NULL,
+ `img_major_mime` enum('unknown','application','audio','image','text','video','message','model','multipart') NOT NULL DEFAULT 'unknown',
+ `img_minor_mime` varbinary(100) NOT NULL DEFAULT 'unknown',
+ `img_description` tinyblob NOT NULL,
+ `img_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `img_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `img_timestamp` varbinary(14) NOT NULL DEFAULT '',
+ `img_sha1` varbinary(32) NOT NULL DEFAULT '',
+ PRIMARY KEY (`img_name`),
+ KEY `img_usertext_timestamp` (`img_user_text`,`img_timestamp`),
+ KEY `img_size` (`img_size`),
+ KEY `img_timestamp` (`img_timestamp`),
+ KEY `img_sha1` (`img_sha1`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_image`
+--
+
+LOCK TABLES `mw_image` WRITE;
+/*!40000 ALTER TABLE `mw_image` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_image` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_imagelinks`
+--
+
+DROP TABLE IF EXISTS `mw_imagelinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_imagelinks` (
+ `il_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `il_to` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ UNIQUE KEY `il_from` (`il_from`,`il_to`),
+ UNIQUE KEY `il_to` (`il_to`,`il_from`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_imagelinks`
+--
+
+LOCK TABLES `mw_imagelinks` WRITE;
+/*!40000 ALTER TABLE `mw_imagelinks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_imagelinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_interwiki`
+--
+
+DROP TABLE IF EXISTS `mw_interwiki`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_interwiki` (
+ `iw_prefix` varchar(32) NOT NULL,
+ `iw_url` blob NOT NULL,
+ `iw_api` blob NOT NULL,
+ `iw_wikiid` varchar(64) NOT NULL,
+ `iw_local` tinyint(1) NOT NULL,
+ `iw_trans` tinyint(4) NOT NULL DEFAULT '0',
+ UNIQUE KEY `iw_prefix` (`iw_prefix`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_interwiki`
+--
+
+LOCK TABLES `mw_interwiki` WRITE;
+/*!40000 ALTER TABLE `mw_interwiki` DISABLE KEYS */;
+INSERT INTO `mw_interwiki` VALUES ('acronym','http://www.acronymfinder.com/af-query.asp?String=exact&Acronym=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('advogato','http://www.advogato.org/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('annotationwiki','http://www.seedwiki.com/page.cfm?wikiid=368&doc=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('arxiv','http://www.arxiv.org/abs/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('c2find','http://c2.com/cgi/wiki?FindPage&value=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('cache','http://www.google.com/search?q=cache:$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('commons','http://commons.wikimedia.org/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('corpknowpedia','http://corpknowpedia.org/wiki/index.php/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('dictionary','http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('disinfopedia','http://www.disinfopedia.org/wiki.phtml?title=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('docbook','http://wiki.docbook.org/topic/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('doi','http://dx.doi.org/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('drumcorpswiki','http://www.drumcorpswiki.com/index.php/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('dwjwiki','http://www.suberic.net/cgi-bin/dwj/wiki.cgi?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('elibre','http://enciclopedia.us.es/index.php/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('emacswiki','http://www.emacswiki.org/cgi-bin/wiki.pl?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('foldoc','http://foldoc.org/?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('foxwiki','http://fox.wikis.com/wc.dll?Wiki~$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('freebsdman','http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('gej','http://www.esperanto.de/cgi-bin/aktivikio/wiki.pl?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('gentoo-wiki','http://gentoo-wiki.com/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('google','http://www.google.com/search?q=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('googlegroups','http://groups.google.com/groups?q=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('hammondwiki','http://www.dairiki.org/HammondWiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('hewikisource','http://he.wikisource.org/wiki/$1','','',1,0);
+INSERT INTO `mw_interwiki` VALUES ('hrwiki','http://www.hrwiki.org/index.php/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('imdb','http://us.imdb.com/Title?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('jargonfile','http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('jspwiki','http://www.jspwiki.org/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('keiki','http://kei.ki/en/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('kmwiki','http://kmwiki.wikispaces.com/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('linuxwiki','http://linuxwiki.de/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('lojban','http://www.lojban.org/tiki/tiki-index.php?page=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('lqwiki','http://wiki.linuxquestions.org/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('lugkr','http://lug-kr.sourceforge.net/cgi-bin/lugwiki.pl?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('mathsongswiki','http://SeedWiki.com/page.cfm?wikiid=237&doc=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('meatball','http://www.usemod.com/cgi-bin/mb.pl?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('mediawikiwiki','http://www.mediawiki.org/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('mediazilla','https://bugzilla.wikimedia.org/$1','','',1,0);
+INSERT INTO `mw_interwiki` VALUES ('memoryalpha','http://www.memory-alpha.org/en/index.php/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('metawiki','http://sunir.org/apps/meta.pl?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('metawikipedia','http://meta.wikimedia.org/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('moinmoin','http://purl.net/wiki/moin/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('mozillawiki','http://wiki.mozilla.org/index.php/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('mw','http://www.mediawiki.org/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('oeis','http://www.research.att.com/cgi-bin/access.cgi/as/njas/sequences/eisA.cgi?Anum=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('openfacts','http://openfacts.berlios.de/index.phtml?title=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('openwiki','http://openwiki.com/?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('pmeg','http://www.bertilow.com/pmeg/$1.php','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('ppr','http://c2.com/cgi/wiki?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('pythoninfo','http://wiki.python.org/moin/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('rfc','http://www.rfc-editor.org/rfc/rfc$1.txt','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('s23wiki','http://is-root.de/wiki/index.php/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('seattlewiki','http://seattle.wikia.com/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('seattlewireless','http://seattlewireless.net/?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('senseislibrary','http://senseis.xmp.net/?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('sourceforge','http://sourceforge.net/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('squeak','http://wiki.squeak.org/squeak/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('susning','http://www.susning.nu/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('svgwiki','http://wiki.svg.org/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('tavi','http://tavi.sourceforge.net/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('tejo','http://www.tejo.org/vikio/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('theopedia','http://www.theopedia.com/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('tmbw','http://www.tmbw.net/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('tmnet','http://www.technomanifestos.net/?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('tmwiki','http://www.EasyTopicMaps.com/?page=$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('twiki','http://twiki.org/cgi-bin/view/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('uea','http://www.tejo.org/uea/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('unreal','http://wiki.beyondunreal.com/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('usemod','http://www.usemod.com/cgi-bin/wiki.pl?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('vinismo','http://vinismo.com/en/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('webseitzwiki','http://webseitz.fluxent.com/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('why','http://clublet.com/c/c/why?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('wiki','http://c2.com/cgi/wiki?$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('wikia','http://www.wikia.com/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('wikibooks','http://en.wikibooks.org/wiki/$1','','',1,0);
+INSERT INTO `mw_interwiki` VALUES ('wikicities','http://www.wikia.com/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('wikif1','http://www.wikif1.org/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('wikihow','http://www.wikihow.com/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('wikimedia','http://wikimediafoundation.org/wiki/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('wikinews','http://en.wikinews.org/wiki/$1','','',1,0);
+INSERT INTO `mw_interwiki` VALUES ('wikinfo','http://www.wikinfo.org/index.php/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('wikipedia','http://en.wikipedia.org/wiki/$1','','',1,0);
+INSERT INTO `mw_interwiki` VALUES ('wikiquote','http://en.wikiquote.org/wiki/$1','','',1,0);
+INSERT INTO `mw_interwiki` VALUES ('wikisource','http://wikisource.org/wiki/$1','','',1,0);
+INSERT INTO `mw_interwiki` VALUES ('wikispecies','http://species.wikimedia.org/wiki/$1','','',1,0);
+INSERT INTO `mw_interwiki` VALUES ('wikitravel','http://wikitravel.org/en/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('wikiversity','http://en.wikiversity.org/wiki/$1','','',1,0);
+INSERT INTO `mw_interwiki` VALUES ('wikt','http://en.wiktionary.org/wiki/$1','','',1,0);
+INSERT INTO `mw_interwiki` VALUES ('wiktionary','http://en.wiktionary.org/wiki/$1','','',1,0);
+INSERT INTO `mw_interwiki` VALUES ('wlug','http://www.wlug.org.nz/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('zwiki','http://zwiki.org/$1','','',0,0);
+INSERT INTO `mw_interwiki` VALUES ('zzz wiki','http://wiki.zzz.ee/index.php/$1','','',0,0);
+/*!40000 ALTER TABLE `mw_interwiki` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_ipblocks`
+--
+
+DROP TABLE IF EXISTS `mw_ipblocks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_ipblocks` (
+ `ipb_id` int(11) NOT NULL AUTO_INCREMENT,
+ `ipb_address` tinyblob NOT NULL,
+ `ipb_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `ipb_by` int(10) unsigned NOT NULL DEFAULT '0',
+ `ipb_by_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `ipb_reason` tinyblob NOT NULL,
+ `ipb_timestamp` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `ipb_auto` tinyint(1) NOT NULL DEFAULT '0',
+ `ipb_anon_only` tinyint(1) NOT NULL DEFAULT '0',
+ `ipb_create_account` tinyint(1) NOT NULL DEFAULT '1',
+ `ipb_enable_autoblock` tinyint(1) NOT NULL DEFAULT '1',
+ `ipb_expiry` varbinary(14) NOT NULL DEFAULT '',
+ `ipb_range_start` tinyblob NOT NULL,
+ `ipb_range_end` tinyblob NOT NULL,
+ `ipb_deleted` tinyint(1) NOT NULL DEFAULT '0',
+ `ipb_block_email` tinyint(1) NOT NULL DEFAULT '0',
+ `ipb_allow_usertalk` tinyint(1) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ipb_id`),
+ UNIQUE KEY `ipb_address` (`ipb_address`(255),`ipb_user`,`ipb_auto`,`ipb_anon_only`),
+ KEY `ipb_user` (`ipb_user`),
+ KEY `ipb_range` (`ipb_range_start`(8),`ipb_range_end`(8)),
+ KEY `ipb_timestamp` (`ipb_timestamp`),
+ KEY `ipb_expiry` (`ipb_expiry`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_ipblocks`
+--
+
+LOCK TABLES `mw_ipblocks` WRITE;
+/*!40000 ALTER TABLE `mw_ipblocks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_ipblocks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_iwlinks`
+--
+
+DROP TABLE IF EXISTS `mw_iwlinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_iwlinks` (
+ `iwl_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `iwl_prefix` varbinary(20) NOT NULL DEFAULT '',
+ `iwl_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ UNIQUE KEY `iwl_from` (`iwl_from`,`iwl_prefix`,`iwl_title`),
+ UNIQUE KEY `iwl_prefix_title_from` (`iwl_prefix`,`iwl_title`,`iwl_from`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_iwlinks`
+--
+
+LOCK TABLES `mw_iwlinks` WRITE;
+/*!40000 ALTER TABLE `mw_iwlinks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_iwlinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_job`
+--
+
+DROP TABLE IF EXISTS `mw_job`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_job` (
+ `job_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `job_cmd` varbinary(60) NOT NULL DEFAULT '',
+ `job_namespace` int(11) NOT NULL,
+ `job_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `job_params` blob NOT NULL,
+ PRIMARY KEY (`job_id`),
+ KEY `job_cmd` (`job_cmd`,`job_namespace`,`job_title`,`job_params`(128))
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_job`
+--
+
+LOCK TABLES `mw_job` WRITE;
+/*!40000 ALTER TABLE `mw_job` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_job` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_l10n_cache`
+--
+
+DROP TABLE IF EXISTS `mw_l10n_cache`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_l10n_cache` (
+ `lc_lang` varbinary(32) NOT NULL,
+ `lc_key` varchar(255) NOT NULL,
+ `lc_value` mediumblob NOT NULL,
+ KEY `lc_lang_key` (`lc_lang`,`lc_key`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+
+
+--
+-- Table structure for table `mw_langlinks`
+--
+
+DROP TABLE IF EXISTS `mw_langlinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_langlinks` (
+ `ll_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `ll_lang` varbinary(20) NOT NULL DEFAULT '',
+ `ll_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ UNIQUE KEY `ll_from` (`ll_from`,`ll_lang`),
+ KEY `ll_lang` (`ll_lang`,`ll_title`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_langlinks`
+--
+
+LOCK TABLES `mw_langlinks` WRITE;
+/*!40000 ALTER TABLE `mw_langlinks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_langlinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_log_search`
+--
+
+DROP TABLE IF EXISTS `mw_log_search`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_log_search` (
+ `ls_field` varbinary(32) NOT NULL,
+ `ls_value` varchar(255) NOT NULL,
+ `ls_log_id` int(10) unsigned NOT NULL DEFAULT '0',
+ UNIQUE KEY `ls_field_val` (`ls_field`,`ls_value`,`ls_log_id`),
+ KEY `ls_log_id` (`ls_log_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_log_search`
+--
+
+LOCK TABLES `mw_log_search` WRITE;
+/*!40000 ALTER TABLE `mw_log_search` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_log_search` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_logging`
+--
+
+DROP TABLE IF EXISTS `mw_logging`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_logging` (
+ `log_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `log_type` varbinary(32) NOT NULL DEFAULT '',
+ `log_action` varbinary(32) NOT NULL DEFAULT '',
+ `log_timestamp` binary(14) NOT NULL DEFAULT '19700101000000',
+ `log_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `log_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `log_namespace` int(11) NOT NULL DEFAULT '0',
+ `log_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `log_page` int(10) unsigned DEFAULT NULL,
+ `log_comment` varchar(255) NOT NULL DEFAULT '',
+ `log_params` blob NOT NULL,
+ `log_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`log_id`),
+ KEY `type_time` (`log_type`,`log_timestamp`),
+ KEY `user_time` (`log_user`,`log_timestamp`),
+ KEY `page_time` (`log_namespace`,`log_title`,`log_timestamp`),
+ KEY `times` (`log_timestamp`),
+ KEY `log_user_type_time` (`log_user`,`log_type`,`log_timestamp`),
+ KEY `log_page_id_time` (`log_page`,`log_timestamp`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_logging`
+--
+
+LOCK TABLES `mw_logging` WRITE;
+/*!40000 ALTER TABLE `mw_logging` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_logging` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_math`
+--
+
+DROP TABLE IF EXISTS `mw_math`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_math` (
+ `math_inputhash` varbinary(16) NOT NULL,
+ `math_outputhash` varbinary(16) NOT NULL,
+ `math_html_conservativeness` tinyint(4) NOT NULL,
+ `math_html` text,
+ `math_mathml` text,
+ UNIQUE KEY `math_inputhash` (`math_inputhash`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_math`
+--
+
+LOCK TABLES `mw_math` WRITE;
+/*!40000 ALTER TABLE `mw_math` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_math` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_module_deps`
+--
+
+DROP TABLE IF EXISTS `mw_module_deps`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_module_deps` (
+ `md_module` varbinary(255) NOT NULL,
+ `md_skin` varbinary(32) NOT NULL,
+ `md_deps` mediumblob NOT NULL,
+ UNIQUE KEY `md_module_skin` (`md_module`,`md_skin`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_module_deps`
+--
+
+LOCK TABLES `mw_module_deps` WRITE;
+/*!40000 ALTER TABLE `mw_module_deps` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_module_deps` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_msg_resource`
+--
+
+DROP TABLE IF EXISTS `mw_msg_resource`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_msg_resource` (
+ `mr_resource` varbinary(255) NOT NULL,
+ `mr_lang` varbinary(32) NOT NULL,
+ `mr_blob` mediumblob NOT NULL,
+ `mr_timestamp` binary(14) NOT NULL,
+ UNIQUE KEY `mr_resource_lang` (`mr_resource`,`mr_lang`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_msg_resource`
+--
+
+LOCK TABLES `mw_msg_resource` WRITE;
+/*!40000 ALTER TABLE `mw_msg_resource` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_msg_resource` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_msg_resource_links`
+--
+
+DROP TABLE IF EXISTS `mw_msg_resource_links`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_msg_resource_links` (
+ `mrl_resource` varbinary(255) NOT NULL,
+ `mrl_message` varbinary(255) NOT NULL,
+ UNIQUE KEY `mrl_message_resource` (`mrl_message`,`mrl_resource`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_msg_resource_links`
+--
+
+LOCK TABLES `mw_msg_resource_links` WRITE;
+/*!40000 ALTER TABLE `mw_msg_resource_links` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_msg_resource_links` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_objectcache`
+--
+
+DROP TABLE IF EXISTS `mw_objectcache`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_objectcache` (
+ `keyname` varbinary(255) NOT NULL DEFAULT '',
+ `value` mediumblob,
+ `exptime` datetime DEFAULT NULL,
+ PRIMARY KEY (`keyname`),
+ KEY `exptime` (`exptime`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_objectcache`
+--
+
+LOCK TABLES `mw_objectcache` WRITE;
+/*!40000 ALTER TABLE `mw_objectcache` DISABLE KEYS */;
+INSERT INTO `mw_objectcache` VALUES ('test_wiki-mw_:messages:en','K´2´ª.¶2·R\ns\r\nöô÷S²Î´2´®\0','2010-12-31 13:16:31');
+INSERT INTO `mw_objectcache` VALUES ('test_wiki-mw_:pcache:idhash:1-0!*!*!!en!*','¥V[oâFÞg~ÅÄ­´ØBšÁP­¢m)$TK»U…û\0£ŒÇÖÌ8$íï9ƒa\rI«l— X>×ïܹãq—Sa,˜»Ê•• x÷Œ?[~ʃ|.ZÇç$å8YŒ\'IñYÞK¶–-\04³Uš‚µËJ©\'&µuB)È:I¸·’µÊñE¡m¥sk`‰`kËQ°v®äa˜ƒ\rZÌÉt§0«ÞÂ+P%GEÚÙ€¥JX;\n-sŽP£@ËB©bŒÿÀ8~¶ì²’$¡³eaþÏ…“…fø­¬Ô+„Ü0[,ÝFèx¬­dÝ\'¶zï0“ÎBJÚÁø¯=ø°Äø¡JÁc§\\—¿:éŒ&BêùT¬à\'‘—CáõFdÿ׆FqÀ¶âÁGd±šÌÙ%8Gè0‡ÆAŒI“ù; Ô˜`Ë7í5ˆLI\r“Ù(¨õç{ýc‹¬¶“„g+©Ô8Qrü¢&›Í¦ãËA)úV’‰Ð•PT”¥\\UƧtn·ÞZ¢e¶SfJZ(VýP¿}øý¾µ0êü—O‹N —=jÏ\\H• íyÔ\\áU[h]T:… À†bd›êu”+Õj%\'í6kÝÂf:E;Ç@YךâÓ4…ØïºÈ€¥ÃqÖ ãÈZÓº6<bñ3œˆTU(dåà,\n»ÑùY|Úe‹\'¶5ƒ¼TÂfUå8}ÿ\"÷ñ±må°Ò/¶ÄÈ}Ukç9oµÛ;äŸÄó|*R?…nÍîá 3dÝÜg1ßÌyé¹\\f8gk·£“÷øwúä=£›:/Y7Š£¸Û‹â^<èÅŒüù­ÕÇv#ôªÂi¼‘úÞCÁ#þü•6\Zò.0ÆUa$4í=\ZÍÝò;çÍ4£™¦÷Y=²æ•ê¬5éÝý:kpÎq£Å¦4áýX·ËÞCŸ–qYßµ-‡’LjõôœÿÐD×fúÑÍÐ Ö:ƒÿáò¦(3tŒ®14Cü×J‰#ñ©ÞÑƵΞWXT®Ûâ’Δy: ^á6½v7ìÿ‚äI‘UêEe·óª(šp0ãùgaô6MjÂScþ,Ñ«@ëÏÞ…+R ö•ãAýŽÎxÐ¥\'6ßôutÇ·bÛ›ºÊ`jµ8ØšòGªIC<KSäþ5š|¾krJ\ry±\\b3xPƒ³­¡ÓuaŽ@ìÎðë®$SS”`ÜÁtQ.gwW³É\r@\'ôè¢ÕwÍß ï°ØxZ(™>5{Ñ Áþ§dwå>·=J)\rÓ6t ·ÅXø̸æM¯É­B»\n÷Å–TÃýŒõb>‘qgÐéï„ 7Ô ›z·n7‘§vwr¸-%uÛ-QiþiX1çöNe±’éA#ÎvÛíÓ§ý3Ìó?','2010-12-31 13:16:31');
+INSERT INTO `mw_objectcache` VALUES ('test_wiki-mw_:pcache:idoptions:1','EÁ‚@ Dÿ¥`¶ \"vÆ£á¢ÞWh°‰,†b¢!ü»»â­™7íL+Ú|}ç³t ´¥I©$è®<¨ôœF\rpSlò4£‰ìïOJN`\r\Z´™Á ‹ Õ®¾ãû)ÃœPYäÆ$¶K¬£— Õ£9ùVjp72ëâE¹©žcÌWpž2šäcVxu7îì ­¦»˜p#£r=.µ…¬[>y)Zpóü','2010-12-31 13:16:31');
+INSERT INTO `mw_objectcache` VALUES ('test_wiki-mw_:resourceloader:filter:minify-css:3832ee25d9c44988461f5f339b9b6a48','+¶26²RrÈMMÉLTHÌÉ©V\0‚Z(¿ (3¯(R«d\r\0','2038-01-19 03:14:07');
+INSERT INTO `mw_objectcache` VALUES ('test_wiki-mw_:resourceloader:filter:minify-css:aa0df16258ad99a1d249e796b5067ed9','+¶2±°RrÈMMÉLTHÌÉ©NÔËK-×Q.,ÍLÎNJ,R\0ó«“ósò‹¬”“\r€ ¶VÉ\Z\0','2038-01-19 03:14:07');
+INSERT INTO `mw_objectcache` VALUES ('test_wiki-mw_:resourceloader:filter:minify-js:22814eeadc9cf0a9ebcd844e14198e66','m”ÉrÛ0 †ïy žÚÇrê&Qޡמ!\n’qQ¹Xq;}÷‚¤$ËÃÞˆ øc!]]].o5SØ\nø)Fq íÑL^íŽý—?Œs…F£!«OÆM\\¼¦•öøéù\0–ÈåN¤Ðɵà‘լłôŒ€è:£å-…j…ƒF¦Ø{Û…¾Gç\"ižø‡ \ZÝ6’KÁÇÍÏ!ÊY]=ØF[Ñ~ç«ÚØ䶃¯ÚÖî`¬Š9NÐÇ´ª•Ï@¹Káü²|zÔí¶±1«AÆÔ@J#_Ôæ7\'úlË1) J͵ê).’3 zÔfÖT†A´˜²HÑšÀ[#)ðBzRA©7ÖŒ˜ë\"TÔ*~SWÎöå‘/Pà‡ä’B®ÅŽ;\Zç”ayƒ6ø€ëÚè+UŠº?.$º6ÀÇ-uTƒv@h…îsÉ&ª¹ÀÙèNØ¥bòfJ’~ê]6–·p£³/q)…>ŸE…1úÎÍ”A\neÍL®g\ZE‡`cW’ÿ™¶Ü`fJ©EÍa‚ˆ>‚šb\n¹Ó‘dÑ.u•doܾ[¹\nt£ b³+õ†l\Z?X* ‘Y•(äÖ…;ßL¶JqÅ¥ÉõÀòd$Ü\"¤WzGûŽ-@b~+‰#™kÇžÙŽÅÆ‚~ˆ¶âÆøÿP)B ï£ø€ã¬ðqŒÒ–2×åÍríRl묀ô`z º4«ÛúÃXímÀ;¨XÝt;r.ÈsA¾äRïy)ÈkAÞ\nRJTª®JÙU©»*…W«ò¿_ߟžîŸ¼4@óvtžþfúà÷>•«½±x„½ÿ','2038-01-19 03:14:07');
+INSERT INTO `mw_objectcache` VALUES ('test_wiki-mw_:resourceloader:filter:minify-js:dd9440c19c575629ac5ec90e489cf62e','+¶21·RâÜÔ”ÌÄðÌìL½œüÄ”Ô\"½â’Ä’Tj¥âÌ’T%+¥¢ÔÄ”J¥ZMk.%k\0','2038-01-19 03:14:07');
+/*!40000 ALTER TABLE `mw_objectcache` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_oldimage`
+--
+
+DROP TABLE IF EXISTS `mw_oldimage`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_oldimage` (
+ `oi_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `oi_archive_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `oi_size` int(10) unsigned NOT NULL DEFAULT '0',
+ `oi_width` int(11) NOT NULL DEFAULT '0',
+ `oi_height` int(11) NOT NULL DEFAULT '0',
+ `oi_bits` int(11) NOT NULL DEFAULT '0',
+ `oi_description` tinyblob NOT NULL,
+ `oi_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `oi_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `oi_timestamp` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `oi_metadata` mediumblob NOT NULL,
+ `oi_media_type` enum('UNKNOWN','BITMAP','DRAWING','AUDIO','VIDEO','MULTIMEDIA','OFFICE','TEXT','EXECUTABLE','ARCHIVE') DEFAULT NULL,
+ `oi_major_mime` enum('unknown','application','audio','image','text','video','message','model','multipart') NOT NULL DEFAULT 'unknown',
+ `oi_minor_mime` varbinary(100) NOT NULL DEFAULT 'unknown',
+ `oi_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `oi_sha1` varbinary(32) NOT NULL DEFAULT '',
+ KEY `oi_usertext_timestamp` (`oi_user_text`,`oi_timestamp`),
+ KEY `oi_name_timestamp` (`oi_name`,`oi_timestamp`),
+ KEY `oi_name_archive_name` (`oi_name`,`oi_archive_name`(14)),
+ KEY `oi_sha1` (`oi_sha1`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_oldimage`
+--
+
+LOCK TABLES `mw_oldimage` WRITE;
+/*!40000 ALTER TABLE `mw_oldimage` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_oldimage` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_page`
+--
+
+DROP TABLE IF EXISTS `mw_page`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_page` (
+ `page_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `page_namespace` int(11) NOT NULL,
+ `page_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `page_restrictions` tinyblob NOT NULL,
+ `page_counter` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `page_is_redirect` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `page_is_new` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `page_random` double unsigned NOT NULL,
+ `page_touched` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `page_latest` int(10) unsigned NOT NULL,
+ `page_len` int(10) unsigned NOT NULL,
+ PRIMARY KEY (`page_id`),
+ UNIQUE KEY `name_title` (`page_namespace`,`page_title`),
+ KEY `page_random` (`page_random`),
+ KEY `page_len` (`page_len`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_page`
+--
+
+LOCK TABLES `mw_page` WRITE;
+/*!40000 ALTER TABLE `mw_page` DISABLE KEYS */;
+INSERT INTO `mw_page` VALUES (1,0,'Main_Page','',1,0,1,0.334989576352,'20101230131547',1,438);
+/*!40000 ALTER TABLE `mw_page` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_page_props`
+--
+
+DROP TABLE IF EXISTS `mw_page_props`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_page_props` (
+ `pp_page` int(11) NOT NULL,
+ `pp_propname` varbinary(60) NOT NULL,
+ `pp_value` blob NOT NULL,
+ UNIQUE KEY `pp_page_propname` (`pp_page`,`pp_propname`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_page_props`
+--
+
+LOCK TABLES `mw_page_props` WRITE;
+/*!40000 ALTER TABLE `mw_page_props` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_page_props` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_page_restrictions`
+--
+
+DROP TABLE IF EXISTS `mw_page_restrictions`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_page_restrictions` (
+ `pr_page` int(11) NOT NULL,
+ `pr_type` varbinary(60) NOT NULL,
+ `pr_level` varbinary(60) NOT NULL,
+ `pr_cascade` tinyint(4) NOT NULL,
+ `pr_user` int(11) DEFAULT NULL,
+ `pr_expiry` varbinary(14) DEFAULT NULL,
+ `pr_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY (`pr_id`),
+ UNIQUE KEY `pr_pagetype` (`pr_page`,`pr_type`),
+ KEY `pr_typelevel` (`pr_type`,`pr_level`),
+ KEY `pr_level` (`pr_level`),
+ KEY `pr_cascade` (`pr_cascade`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_page_restrictions`
+--
+
+LOCK TABLES `mw_page_restrictions` WRITE;
+/*!40000 ALTER TABLE `mw_page_restrictions` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_page_restrictions` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_pagelinks`
+--
+
+DROP TABLE IF EXISTS `mw_pagelinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_pagelinks` (
+ `pl_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `pl_namespace` int(11) NOT NULL DEFAULT '0',
+ `pl_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ UNIQUE KEY `pl_from` (`pl_from`,`pl_namespace`,`pl_title`),
+ UNIQUE KEY `pl_namespace` (`pl_namespace`,`pl_title`,`pl_from`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_pagelinks`
+--
+
+LOCK TABLES `mw_pagelinks` WRITE;
+/*!40000 ALTER TABLE `mw_pagelinks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_pagelinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_protected_titles`
+--
+
+DROP TABLE IF EXISTS `mw_protected_titles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_protected_titles` (
+ `pt_namespace` int(11) NOT NULL,
+ `pt_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `pt_user` int(10) unsigned NOT NULL,
+ `pt_reason` tinyblob,
+ `pt_timestamp` binary(14) NOT NULL,
+ `pt_expiry` varbinary(14) NOT NULL DEFAULT '',
+ `pt_create_perm` varbinary(60) NOT NULL,
+ UNIQUE KEY `pt_namespace_title` (`pt_namespace`,`pt_title`),
+ KEY `pt_timestamp` (`pt_timestamp`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_protected_titles`
+--
+
+LOCK TABLES `mw_protected_titles` WRITE;
+/*!40000 ALTER TABLE `mw_protected_titles` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_protected_titles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_querycache`
+--
+
+DROP TABLE IF EXISTS `mw_querycache`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_querycache` (
+ `qc_type` varbinary(32) NOT NULL,
+ `qc_value` int(10) unsigned NOT NULL DEFAULT '0',
+ `qc_namespace` int(11) NOT NULL DEFAULT '0',
+ `qc_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ KEY `qc_type` (`qc_type`,`qc_value`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_querycache`
+--
+
+LOCK TABLES `mw_querycache` WRITE;
+/*!40000 ALTER TABLE `mw_querycache` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_querycache` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_querycache_info`
+--
+
+DROP TABLE IF EXISTS `mw_querycache_info`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_querycache_info` (
+ `qci_type` varbinary(32) NOT NULL DEFAULT '',
+ `qci_timestamp` binary(14) NOT NULL DEFAULT '19700101000000',
+ UNIQUE KEY `qci_type` (`qci_type`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_querycache_info`
+--
+
+LOCK TABLES `mw_querycache_info` WRITE;
+/*!40000 ALTER TABLE `mw_querycache_info` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_querycache_info` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_querycachetwo`
+--
+
+DROP TABLE IF EXISTS `mw_querycachetwo`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_querycachetwo` (
+ `qcc_type` varbinary(32) NOT NULL,
+ `qcc_value` int(10) unsigned NOT NULL DEFAULT '0',
+ `qcc_namespace` int(11) NOT NULL DEFAULT '0',
+ `qcc_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `qcc_namespacetwo` int(11) NOT NULL DEFAULT '0',
+ `qcc_titletwo` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ KEY `qcc_type` (`qcc_type`,`qcc_value`),
+ KEY `qcc_title` (`qcc_type`,`qcc_namespace`,`qcc_title`),
+ KEY `qcc_titletwo` (`qcc_type`,`qcc_namespacetwo`,`qcc_titletwo`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_querycachetwo`
+--
+
+LOCK TABLES `mw_querycachetwo` WRITE;
+/*!40000 ALTER TABLE `mw_querycachetwo` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_querycachetwo` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_recentchanges`
+--
+
+DROP TABLE IF EXISTS `mw_recentchanges`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_recentchanges` (
+ `rc_id` int(11) NOT NULL AUTO_INCREMENT,
+ `rc_timestamp` varbinary(14) NOT NULL DEFAULT '',
+ `rc_cur_time` varbinary(14) NOT NULL DEFAULT '',
+ `rc_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `rc_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
+ `rc_namespace` int(11) NOT NULL DEFAULT '0',
+ `rc_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `rc_comment` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `rc_minor` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_bot` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_new` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_cur_id` int(10) unsigned NOT NULL DEFAULT '0',
+ `rc_this_oldid` int(10) unsigned NOT NULL DEFAULT '0',
+ `rc_last_oldid` int(10) unsigned NOT NULL DEFAULT '0',
+ `rc_type` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_moved_to_ns` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_moved_to_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `rc_patrolled` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_ip` varbinary(40) NOT NULL DEFAULT '',
+ `rc_old_len` int(11) DEFAULT NULL,
+ `rc_new_len` int(11) DEFAULT NULL,
+ `rc_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rc_logid` int(10) unsigned NOT NULL DEFAULT '0',
+ `rc_log_type` varbinary(255) DEFAULT NULL,
+ `rc_log_action` varbinary(255) DEFAULT NULL,
+ `rc_params` blob,
+ PRIMARY KEY (`rc_id`),
+ KEY `rc_timestamp` (`rc_timestamp`),
+ KEY `rc_namespace_title` (`rc_namespace`,`rc_title`),
+ KEY `rc_cur_id` (`rc_cur_id`),
+ KEY `new_name_timestamp` (`rc_new`,`rc_namespace`,`rc_timestamp`),
+ KEY `rc_ip` (`rc_ip`),
+ KEY `rc_ns_usertext` (`rc_namespace`,`rc_user_text`),
+ KEY `rc_user_text` (`rc_user_text`,`rc_timestamp`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_recentchanges`
+--
+
+LOCK TABLES `mw_recentchanges` WRITE;
+/*!40000 ALTER TABLE `mw_recentchanges` DISABLE KEYS */;
+INSERT INTO `mw_recentchanges` VALUES (1,'20101230131547','20101230131547',0,'MediaWiki Default',0,'Main_Page','',0,0,1,1,1,0,1,0,'',0,'::1',0,438,0,0,NULL,'','');
+/*!40000 ALTER TABLE `mw_recentchanges` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_redirect`
+--
+
+DROP TABLE IF EXISTS `mw_redirect`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_redirect` (
+ `rd_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `rd_namespace` int(11) NOT NULL DEFAULT '0',
+ `rd_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `rd_interwiki` varchar(32) DEFAULT NULL,
+ `rd_fragment` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
+ PRIMARY KEY (`rd_from`),
+ KEY `rd_ns_title` (`rd_namespace`,`rd_title`,`rd_from`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_redirect`
+--
+
+LOCK TABLES `mw_redirect` WRITE;
+/*!40000 ALTER TABLE `mw_redirect` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_redirect` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_revision`
+--
+
+DROP TABLE IF EXISTS `mw_revision`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_revision` (
+ `rev_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `rev_page` int(10) unsigned NOT NULL,
+ `rev_text_id` int(10) unsigned NOT NULL,
+ `rev_comment` tinyblob NOT NULL,
+ `rev_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `rev_user_text` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `rev_timestamp` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `rev_minor_edit` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rev_deleted` tinyint(3) unsigned NOT NULL DEFAULT '0',
+ `rev_len` int(10) unsigned DEFAULT NULL,
+ `rev_parent_id` int(10) unsigned DEFAULT NULL,
+ PRIMARY KEY (`rev_id`),
+ UNIQUE KEY `rev_page_id` (`rev_page`,`rev_id`),
+ KEY `rev_timestamp` (`rev_timestamp`),
+ KEY `page_timestamp` (`rev_page`,`rev_timestamp`),
+ KEY `user_timestamp` (`rev_user`,`rev_timestamp`),
+ KEY `usertext_timestamp` (`rev_user_text`,`rev_timestamp`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1 MAX_ROWS=10000000 AVG_ROW_LENGTH=1024;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_revision`
+--
+
+LOCK TABLES `mw_revision` WRITE;
+/*!40000 ALTER TABLE `mw_revision` DISABLE KEYS */;
+INSERT INTO `mw_revision` VALUES (1,1,1,'',0,'MediaWiki Default','20101230131547',0,0,438,0);
+/*!40000 ALTER TABLE `mw_revision` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_searchindex`
+--
+
+DROP TABLE IF EXISTS `mw_searchindex`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_searchindex` (
+ `si_page` int(10) unsigned NOT NULL,
+ `si_title` varchar(255) NOT NULL DEFAULT '',
+ `si_text` mediumtext NOT NULL,
+ UNIQUE KEY `si_page` (`si_page`),
+ FULLTEXT KEY `si_title` (`si_title`),
+ FULLTEXT KEY `si_text` (`si_text`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_searchindex`
+--
+
+LOCK TABLES `mw_searchindex` WRITE;
+/*!40000 ALTER TABLE `mw_searchindex` DISABLE KEYS */;
+INSERT INTO `mw_searchindex` VALUES (1,'main page',' mediawiki hasu800 been successfully installed. consult theu800 user user\'su800 guide foru800 information onu800 using theu800 wiki software. getting started getting started getting started configuration settings list mediawiki faqu800 mediawiki release mailing list ');
+/*!40000 ALTER TABLE `mw_searchindex` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_site_stats`
+--
+
+DROP TABLE IF EXISTS `mw_site_stats`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_site_stats` (
+ `ss_row_id` int(10) unsigned NOT NULL,
+ `ss_total_views` bigint(20) unsigned DEFAULT '0',
+ `ss_total_edits` bigint(20) unsigned DEFAULT '0',
+ `ss_good_articles` bigint(20) unsigned DEFAULT '0',
+ `ss_total_pages` bigint(20) DEFAULT '-1',
+ `ss_users` bigint(20) DEFAULT '-1',
+ `ss_active_users` bigint(20) DEFAULT '-1',
+ `ss_admins` int(11) DEFAULT '-1',
+ `ss_images` int(11) DEFAULT '0',
+ UNIQUE KEY `ss_row_id` (`ss_row_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_site_stats`
+--
+
+LOCK TABLES `mw_site_stats` WRITE;
+/*!40000 ALTER TABLE `mw_site_stats` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_site_stats` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_tag_summary`
+--
+
+DROP TABLE IF EXISTS `mw_tag_summary`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_tag_summary` (
+ `ts_rc_id` int(11) DEFAULT NULL,
+ `ts_log_id` int(11) DEFAULT NULL,
+ `ts_rev_id` int(11) DEFAULT NULL,
+ `ts_tags` blob NOT NULL,
+ UNIQUE KEY `tag_summary_rc_id` (`ts_rc_id`),
+ UNIQUE KEY `tag_summary_log_id` (`ts_log_id`),
+ UNIQUE KEY `tag_summary_rev_id` (`ts_rev_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_tag_summary`
+--
+
+LOCK TABLES `mw_tag_summary` WRITE;
+/*!40000 ALTER TABLE `mw_tag_summary` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_tag_summary` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_templatelinks`
+--
+
+DROP TABLE IF EXISTS `mw_templatelinks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_templatelinks` (
+ `tl_from` int(10) unsigned NOT NULL DEFAULT '0',
+ `tl_namespace` int(11) NOT NULL DEFAULT '0',
+ `tl_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ UNIQUE KEY `tl_from` (`tl_from`,`tl_namespace`,`tl_title`),
+ UNIQUE KEY `tl_namespace` (`tl_namespace`,`tl_title`,`tl_from`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_templatelinks`
+--
+
+LOCK TABLES `mw_templatelinks` WRITE;
+/*!40000 ALTER TABLE `mw_templatelinks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_templatelinks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_text`
+--
+
+DROP TABLE IF EXISTS `mw_text`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_text` (
+ `old_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `old_text` mediumblob NOT NULL,
+ `old_flags` tinyblob NOT NULL,
+ PRIMARY KEY (`old_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1 MAX_ROWS=10000000 AVG_ROW_LENGTH=10240;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_text`
+--
+
+LOCK TABLES `mw_text` WRITE;
+/*!40000 ALTER TABLE `mw_text` DISABLE KEYS */;
+INSERT INTO `mw_text` VALUES (1,'\'\'\'MediaWiki has been successfully installed.\'\'\'\n\nConsult the [http://meta.wikimedia.org/wiki/Help:Contents User\'s Guide] for information on using the wiki software.\n\n== Getting started ==\n* [http://www.mediawiki.org/wiki/Manual:Configuration_settings Configuration settings list]\n* [http://www.mediawiki.org/wiki/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list]','utf-8');
+/*!40000 ALTER TABLE `mw_text` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_trackbacks`
+--
+
+DROP TABLE IF EXISTS `mw_trackbacks`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_trackbacks` (
+ `tb_id` int(11) NOT NULL AUTO_INCREMENT,
+ `tb_page` int(11) DEFAULT NULL,
+ `tb_title` varchar(255) NOT NULL,
+ `tb_url` blob NOT NULL,
+ `tb_ex` text,
+ `tb_name` varchar(255) DEFAULT NULL,
+ PRIMARY KEY (`tb_id`),
+ KEY `tb_page` (`tb_page`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_trackbacks`
+--
+
+LOCK TABLES `mw_trackbacks` WRITE;
+/*!40000 ALTER TABLE `mw_trackbacks` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_trackbacks` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_transcache`
+--
+
+DROP TABLE IF EXISTS `mw_transcache`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_transcache` (
+ `tc_url` varbinary(255) NOT NULL,
+ `tc_contents` text,
+ `tc_time` binary(14) NOT NULL,
+ UNIQUE KEY `tc_url_idx` (`tc_url`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_transcache`
+--
+
+LOCK TABLES `mw_transcache` WRITE;
+/*!40000 ALTER TABLE `mw_transcache` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_transcache` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_updatelog`
+--
+
+DROP TABLE IF EXISTS `mw_updatelog`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_updatelog` (
+ `ul_key` varchar(255) NOT NULL,
+ `ul_value` blob,
+ PRIMARY KEY (`ul_key`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_updatelog`
+--
+
+LOCK TABLES `mw_updatelog` WRITE;
+/*!40000 ALTER TABLE `mw_updatelog` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_updatelog` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_user`
+--
+
+DROP TABLE IF EXISTS `mw_user`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_user` (
+ `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `user_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `user_real_name` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `user_password` tinyblob NOT NULL,
+ `user_newpassword` tinyblob NOT NULL,
+ `user_newpass_time` binary(14) DEFAULT NULL,
+ `user_email` tinytext NOT NULL,
+ `user_options` blob NOT NULL,
+ `user_touched` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `user_token` binary(32) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ `user_email_authenticated` binary(14) DEFAULT NULL,
+ `user_email_token` binary(32) DEFAULT NULL,
+ `user_email_token_expires` binary(14) DEFAULT NULL,
+ `user_registration` binary(14) DEFAULT NULL,
+ `user_editcount` int(11) DEFAULT NULL,
+ PRIMARY KEY (`user_id`),
+ UNIQUE KEY `user_name` (`user_name`),
+ KEY `user_email_token` (`user_email_token`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_user`
+--
+
+LOCK TABLES `mw_user` WRITE;
+/*!40000 ALTER TABLE `mw_user` DISABLE KEYS */;
+INSERT INTO `mw_user` VALUES (1,'WikiSysop','',':B:b1373470:f7e87db0c9596055f39a1225b0c31508','',NULL,'','','20101230131552','de4ddde7c4eef6e3609f4287324a0a18',NULL,'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',NULL,'20101230131547',0);
+/*!40000 ALTER TABLE `mw_user` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_user_groups`
+--
+
+DROP TABLE IF EXISTS `mw_user_groups`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_user_groups` (
+ `ug_user` int(10) unsigned NOT NULL DEFAULT '0',
+ `ug_group` varbinary(16) NOT NULL DEFAULT '',
+ UNIQUE KEY `ug_user_group` (`ug_user`,`ug_group`),
+ KEY `ug_group` (`ug_group`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_user_groups`
+--
+
+LOCK TABLES `mw_user_groups` WRITE;
+/*!40000 ALTER TABLE `mw_user_groups` DISABLE KEYS */;
+INSERT INTO `mw_user_groups` VALUES (1,'bureaucrat');
+INSERT INTO `mw_user_groups` VALUES (1,'sysop');
+/*!40000 ALTER TABLE `mw_user_groups` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_user_newtalk`
+--
+
+DROP TABLE IF EXISTS `mw_user_newtalk`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_user_newtalk` (
+ `user_id` int(11) NOT NULL DEFAULT '0',
+ `user_ip` varbinary(40) NOT NULL DEFAULT '',
+ `user_last_timestamp` binary(14) NOT NULL DEFAULT '\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
+ KEY `user_id` (`user_id`),
+ KEY `user_ip` (`user_ip`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_user_newtalk`
+--
+
+LOCK TABLES `mw_user_newtalk` WRITE;
+/*!40000 ALTER TABLE `mw_user_newtalk` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_user_newtalk` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_user_properties`
+--
+
+DROP TABLE IF EXISTS `mw_user_properties`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_user_properties` (
+ `up_user` int(11) NOT NULL,
+ `up_property` varbinary(32) NOT NULL,
+ `up_value` blob,
+ UNIQUE KEY `user_properties_user_property` (`up_user`,`up_property`),
+ KEY `user_properties_property` (`up_property`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_user_properties`
+--
+
+LOCK TABLES `mw_user_properties` WRITE;
+/*!40000 ALTER TABLE `mw_user_properties` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_user_properties` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_valid_tag`
+--
+
+DROP TABLE IF EXISTS `mw_valid_tag`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_valid_tag` (
+ `vt_tag` varchar(255) NOT NULL,
+ PRIMARY KEY (`vt_tag`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_valid_tag`
+--
+
+LOCK TABLES `mw_valid_tag` WRITE;
+/*!40000 ALTER TABLE `mw_valid_tag` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_valid_tag` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `mw_watchlist`
+--
+
+DROP TABLE IF EXISTS `mw_watchlist`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `mw_watchlist` (
+ `wl_user` int(10) unsigned NOT NULL,
+ `wl_namespace` int(11) NOT NULL DEFAULT '0',
+ `wl_title` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
+ `wl_notificationtimestamp` varbinary(14) DEFAULT NULL,
+ UNIQUE KEY `wl_user` (`wl_user`,`wl_namespace`,`wl_title`),
+ KEY `namespace_title` (`wl_namespace`,`wl_title`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `mw_watchlist`
+--
+
+LOCK TABLES `mw_watchlist` WRITE;
+/*!40000 ALTER TABLE `mw_watchlist` DISABLE KEYS */;
+/*!40000 ALTER TABLE `mw_watchlist` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2010-12-31 1:20:11
diff --git a/tests/selenium/installer/MediaWikiButtonsAvailabilityTestCase.php b/tests/selenium/installer/MediaWikiButtonsAvailabilityTestCase.php
new file mode 100644
index 00000000..3557f516
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiButtonsAvailabilityTestCase.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * MediaWikiButtonsAvailabilityTestCase
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+
+require_once (dirname(__FILE__).'/'.'MediaWikiInstallationCommonFunction.php');
+
+/*
+ * Test Case ID : 30 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name :'Back' and 'Continue' button availability
+ * Version : MediaWiki 1.18alpha
+*/
+
+
+class MediaWikiButtonsAvailabilityTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+
+ // Verify only 'Continue' button available on 'Language' page
+ public function testOnlyContinueButtonAvailability() {
+
+ parent::navigateLanguagePage();
+
+ // Verify only 'Continue' button avaialble
+ $this->assertTrue( $this->isElementPresent( "submit-continue" ));
+
+ // 'Back' button is not avaialble
+ $this->assertElementNotPresent( "submit-back" );
+ }
+
+
+ // Verify 'Continue' and 'Back' buttons availability
+ public function testBothButtonsAvailability() {
+
+ // Verify buttons availability on 'Welcome to MediaWiki' page
+ parent::navigateWelcometoMediaWikiPage();
+ $this->assertTrue( $this->isElementPresent( "submit-back" ));
+ $this->assertTrue( $this->isElementPresent( "submit-continue" ));
+ parent::restartInstallation();
+
+ // Verify buttons availability on 'Connect to Database' page
+ parent::navigateConnetToDatabasePage();
+ $this->assertTrue( $this->isElementPresent( "submit-back" ));
+ $this->assertTrue( $this->isElementPresent( "submit-continue" ));
+ parent::restartInstallation();
+
+ // Verify buttons availability on 'Database settings' page
+ $databaseName = DB_NAME_PREFIX."_db_settings";
+ parent::navigateDatabaseSettingsPage( $databaseName );
+ $this->assertTrue( $this->isElementPresent( "submit-back" ));
+ $this->assertTrue( $this->isElementPresent( "submit-continue" ));
+ parent::restartInstallation();
+
+ // Verify buttons availability on 'Name' page
+ $databaseName = DB_NAME_PREFIX."_name";
+ parent::navigateNamePage( $databaseName );
+ $this->assertTrue( $this->isElementPresent( "submit-back" ));
+ $this->assertTrue( $this->isElementPresent( "submit-continue" ));
+ parent::restartInstallation();
+
+ // Verify buttons availability on 'Options' page
+ $databaseName = DB_NAME_PREFIX."_options";
+ parent::navigateOptionsPage( $databaseName );
+ $this->assertTrue( $this->isElementPresent( "submit-back" ));
+ $this->assertTrue( $this->isElementPresent( "submit-continue" ));
+ parent::restartInstallation();
+
+ // Verify buttons availability on 'Install' page
+ $databaseName = DB_NAME_PREFIX."_install";
+ parent::navigateInstallPage($databaseName);
+ $this->assertTrue( $this->isElementPresent( "submit-back" ));
+ $this->assertTrue( $this->isElementPresent( "submit-continue" ));
+ }
+} \ No newline at end of file
diff --git a/tests/selenium/installer/MediaWikiDifferentDatabaseAccountTestCase.php b/tests/selenium/installer/MediaWikiDifferentDatabaseAccountTestCase.php
new file mode 100644
index 00000000..4afcdc0e
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiDifferentDatabaseAccountTestCase.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * MediaWikiDifferentDatabaseAccountTestCase
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+
+require_once ( dirname( __FILE__ ) . '/MediaWikiInstallationCommonFunction.php' );
+
+/*
+ * Test Case ID : 04 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name : Install MediaWiki with different Database accounts for web access.
+ * Version : MediaWiki 1.18alpha
+*/
+
+class MediaWikiDifferentDatabaseAccountTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+
+ // Install Mediawiki using 'MySQL' database type.
+ public function testDifferentDatabaseAccount() {
+
+ $databaseName = DB_NAME_PREFIX."_dif_accounts";
+
+ // Navigate to the 'Database settings' page
+ parent::navigateDatabaseSettingsPage( $databaseName );
+
+ // Click on the 'Use the same account as for installation' check box
+ $this->click( "mysql__SameAccount" );
+
+ // Change the 'Database username'
+ $this->type( "mysql_wgDBuser", DB_WEB_USER );
+
+ // Enter 'Database password:'
+ $this->type( "mysql_wgDBpassword", DB_WEB_USER_PASSWORD );
+
+ // Select 'Create the account if it does not already exist' check box
+ $this->click( "mysql__CreateDBAccount" );
+ parent::clickContinueButton();
+
+ // 'Name' page
+ parent::completeNamePage();
+
+ // 'Options' page
+ parent::clickContinueButton();
+
+ // 'Install' page
+ $this->assertEquals("Creating database user... done",
+ $this->getText( LINK_FORM."ul/li[3]"));
+ parent::clickContinueButton();
+
+ // 'Complete' page
+ parent::completePageSuccessfull();
+ $this->chooseCancelOnNextConfirmation();
+ }
+}
diff --git a/tests/selenium/installer/MediaWikiDifferntDatabasePrefixTestCase.php b/tests/selenium/installer/MediaWikiDifferntDatabasePrefixTestCase.php
new file mode 100644
index 00000000..b6a0fc09
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiDifferntDatabasePrefixTestCase.php
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * MediaWikiDifferntDatabasePrefixTestCase
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+require_once ( dirname( __FILE__ ) . '/MediaWikiInstallationCommonFunction.php' );
+
+/*
+ * Test Case ID : 02 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name : Install MediaWiki with the same database and the different
+ * database prefixes(Share one database between multiple wikis).
+ * Version : MediaWiki 1.18alpha
+*/
+
+class MediaWikiDifferntDatabasePrefixTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+ // Install Mediawiki using 'MySQL' database type.
+ public function testDifferentDatabasePrefix() {
+
+ $databaseName = DB_NAME_PREFIX."_db_prefix";
+ parent::navigateInstallPage( $databaseName );
+
+ // To 'Options' page
+ parent::clickBackButton();
+
+ // To 'Name' page
+ parent::clickBackButton();
+
+ // To 'Database settings' page
+ parent::clickBackButton();
+
+ // To 'Connect to database' page
+ parent::clickBackButton();
+
+ // From 'Connect to database' page without database prefix
+ parent::clickContinueButton();
+
+ // Verify upgrade existing message
+ $this->assertEquals( "Upgrade existing installation",
+ $this->getText( LINK_DIV."h2" ));
+
+ // To 'Connect to database' page
+ parent::clickBackButton();
+
+ // Input the database prefix
+ $this->type( "mysql_wgDBprefix", DATABASE_PREFIX );
+
+ // From 'Connect to database' page with database prefix
+ parent::clickContinueButton();
+
+ // To 'Complete' page
+ parent::clickContinueButton();
+ parent::completeNamePage();
+ parent::clickContinueButton();
+
+ // Verify already installed warning message
+ $this->assertEquals( "Install",
+ $this->getText( LINK_DIV."h2" ));
+ $this->assertEquals( "Warning: You seem to have already installed MediaWiki and are trying to install it again. Please proceed to the next page.",
+ $this->getText( LINK_FORM."div[1]" ));
+
+ parent::clickContinueButton();
+ parent::completePageSuccessfull();
+ $this->chooseCancelOnNextConfirmation();
+ parent::restartInstallation();
+ }
+}
diff --git a/tests/selenium/installer/MediaWikiErrorsConnectToDatabasePageTestCase.php b/tests/selenium/installer/MediaWikiErrorsConnectToDatabasePageTestCase.php
new file mode 100644
index 00000000..3642a8ef
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiErrorsConnectToDatabasePageTestCase.php
@@ -0,0 +1,136 @@
+<?php
+
+/**
+ * MediaWikiErrorsConnectToDatabasePageTestCase
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+
+require_once ( dirname( __FILE__ ) . '/MediaWikiInstallationCommonFunction.php' );
+
+/*
+ * Test Case ID : 09 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name : Invalid/ blank values for fields in 'Connect to database' page.
+ * Version : MediaWiki 1.18alpha
+*/
+
+class MediaWikiErrorsConnectToDatabasePageTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+ // Verify warning messages for the 'Connet to database' page
+ public function testErrorsConnectToDatabasePage() {
+
+ parent::navigateConnetToDatabasePage();
+
+ // Verify warning mesage for invalid database host
+ $this->type( "mysql_wgDBserver", INVALID_DB_HOST );
+ parent::clickContinueButton();
+ $this->assertEquals( "DB connection error: php_network_getaddresses: getaddrinfo failed: No such host is known. (".INVALID_DB_HOST.").",
+ $this->getText( LINK_DIV."div[2]/div[2]/p[1]" ));
+ $this->assertEquals( "Check the host, username and password below and try again.",
+ $this->getText( LINK_DIV."div[2]/div[2]/p[2]" ));
+ // Verify warning message for the blank database host
+ $this->type( "mysql_wgDBserver", "" );
+ parent::clickContinueButton();
+ $this->assertEquals( "MySQL 4.0.14 or later is required, you have .",
+ $this->getText( LINK_DIV."div[2]/div[2]" ));
+
+ // Valid Database Host
+ $this->type( "mysql_wgDBserver", VALID_DB_HOST );
+
+ // Verify warning message for the invalid database name
+ $this->type( "mysql_wgDBname", INVALID_DB_NAME );
+ parent::clickContinueButton();
+ $this->assertEquals( "Invalid database name \"".INVALID_DB_NAME."\". Use only ASCII letters (a-z, A-Z), numbers (0-9) and underscores (_).",
+ $this->getText( LINK_DIV."div[2]/div[2]/p" ));
+
+ // Verify warning message for the blank database name
+ $this->type( "mysql_wgDBname", "");
+ parent::clickContinueButton();
+ $this->assertEquals( "You must enter a value for \"Database name\"",
+ $this->getText( LINK_DIV."div[2]/div[2]" ));
+
+ // valid Database name
+ $this->type( "mysql_wgDBname", VALID_DB_NAME);
+
+ // Verify warning message for the invalid databaase prefix
+ $this->type( "mysql_wgDBprefix", INVALID_DB_PREFIX );
+ parent::clickContinueButton();
+ $this->assertEquals( "Invalid database prefix \"".INVALID_DB_PREFIX."\". Use only ASCII letters (a-z, A-Z), numbers (0-9) and underscores (_).",
+ $this->getText( LINK_DIV."div[2]/div[2]" ));
+
+ // Valid Database prefix
+ $this->type( "mysql_wgDBprefix", VALID_DB_PREFIX );
+
+ // Verify warning message for the invalid database user name
+ $this->type( "mysql__InstallUser", INVALID_DB_USER_NAME );
+ parent::clickContinueButton();
+ $this->assertEquals( "DB connection error: Access denied for user '".INVALID_DB_USER_NAME."'@'localhost' (using password: NO) (localhost).",
+ $this->getText( LINK_DIV."div[2]/div[2]/p[1]" ));
+ $this->assertEquals( "Check the host, username and password below and try again.",
+ $this->getText( LINK_DIV."div[2]/div[2]/p[2]"));
+
+ // Verify warning message for the blank database user name
+ $this->type( "mysql__InstallUser", "" );
+ parent::clickContinueButton();
+ $this->assertEquals( "DB connection error: Access denied for user 'SYSTEM'@'localhost' (using password: NO) (localhost).",
+ $this->getText( LINK_DIV."div[2]/div[2]/p[1]" ));
+ $this->assertEquals( "Check the host, username and password below and try again.",
+ $this->getText( LINK_DIV."div[2]/div[2]/p[2]" ));
+
+ // Valid Database username
+ $this->type( "mysql__InstallUser", VALID_DB_USER_NAME );
+
+ // Verify warning message for the invalid password
+ $this->type( "mysql__InstallPassword", INVALID_DB_PASSWORD );
+ parent::clickContinueButton();
+
+ $this->assertEquals( "DB connection error: Access denied for user 'root'@'localhost' (using password: YES) (localhost).",
+ $this->getText( LINK_DIV."div[2]/div[2]/p[1]" ));
+ $this->assertEquals( "Check the host, username and password below and try again.",
+ $this->getText( LINK_DIV."div[2]/div[2]/p[2]" ));
+
+ // Verify warning message for the invalid username and password
+ $this->type( "mysql__InstallUser", INVALID_DB_USER_NAME );
+ $this->type( "mysql__InstallPassword", INVALID_DB_PASSWORD );
+ parent::clickContinueButton();
+ $this->assertEquals( "DB connection error: Access denied for user '".INVALID_DB_USER_NAME."'@'localhost' (using password: YES) (localhost).",
+ $this->getText( LINK_DIV."div[2]/div[2]/p[1]" ));
+ $this->assertEquals( "Check the host, username and password below and try again.",
+ $this->getText( LINK_DIV."div[2]/div[2]/p[2]" ));
+
+ // Valid username and valid password
+ $this->type( "mysql__InstallUser", VALID_DB_USER_NAME );
+ $this->type( "mysql__InstallPassword", "" );
+ parent::clickContinueButton();
+
+ // successfully completes the 'Connect to database' page
+ $this->assertEquals( "Database settings",
+ $this->getText( LINK_DIV."h2" ));
+ }
+}
diff --git a/tests/selenium/installer/MediaWikiErrorsNamepageTestCase.php b/tests/selenium/installer/MediaWikiErrorsNamepageTestCase.php
new file mode 100644
index 00000000..d70dcc42
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiErrorsNamepageTestCase.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * MediaWikiErrorsNamepageTestCase
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+/*
+ * Test Case ID : 10 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name : Invalid/ blank values for fields in 'Name' page.
+ * Version : MediaWiki 1.18alpha
+*/
+
+require_once ( dirname( __FILE__ ) . '/MediaWikiInstallationCommonFunction.php' );
+
+class MediaWikiErrorsNamepageTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+ // Verify warning message for the 'Name' page
+ public function testErrorsNamePage() {
+
+ $databaseName = DB_NAME_PREFIX."_error_name";
+
+ parent::navigateNamePage( $databaseName );
+
+ // Verify warning message for all blank fields
+ parent::clickContinueButton();
+ $this->assertEquals( "Enter a site name.",
+ $this->getText( LINK_DIV."div[2]/div[2]" ));
+ $this->assertEquals( "Enter an administrator username.",
+ $this->getText( LINK_DIV."div[3]/div[2]" ));
+ $this->assertEquals( "Enter a password for the administrator account.",
+ $this->getText( LINK_DIV."div[4]/div[2]" ));
+
+ // Verify warning message for the blank 'Site name'
+ $this->type( "config__AdminName", VALID_YOUR_NAME );
+ $this->type( "config__AdminPassword", VALID_PASSWORD );
+ $this->type( "config__AdminPassword2", VALID_PASSWORD_AGAIN );
+ parent::clickContinueButton();
+ $this->assertEquals( "Enter a site name.",
+ $this->getText( LINK_DIV."div[2]/div[2]" ));
+
+ // Input valid 'Site name'
+ $this->type( "config_wgSitename", VALID_WIKI_NAME );
+
+
+ // Verify warning message for the invalid "Project namespace'
+ $this->click( "config__NamespaceType_other" );
+ $this->type( "config_wgMetaNamespace", INVALID_NAMESPACE );
+ parent::clickContinueButton();
+ $this->assertEquals( "The specified namespace \"\" is invalid. Specify a different project namespace.",
+ $this->getText( LINK_DIV."div[2]/div[2]" ));
+
+
+ // Verify warning message for the blank 'Project namespace'
+ $this->type( "config_wgSitename", VALID_WIKI_NAME );
+ $this->click( "config__NamespaceType_other" );
+ $this->type( "config_wgMetaNamespace" , "" );
+ parent::clickContinueButton();
+ $this->assertEquals( "The specified namespace \"\" is invalid. Specify a different project namespace.",
+ $this->getText( LINK_DIV."div[2]/div[2]" ));
+
+
+ // Valid 'Project namespace'
+ $this->click( "config__NamespaceType_other" );
+ $this->type( "config_wgMetaNamespace", VALID_NAMESPACE );
+ parent::clickContinueButton();
+
+
+ // Valid 'Site name'
+ $this->click( "config__NamespaceType_site-name" );
+ $this->type( "config_wgSitename", VALID_WIKI_NAME );
+
+
+ // Verify warning message for blank 'Your name'
+ $this->type( "config__AdminName", " " );
+ parent::clickContinueButton();
+ $this->assertEquals( "Enter an administrator username.",
+ $this->getText( LINK_DIV."div[2]/div[2]" ));
+
+ $this->type( "config_wgSitename", VALID_WIKI_NAME );
+ // Verify warning message for blank 'Password'
+ $this->type( "config__AdminName", VALID_YOUR_NAME );
+ $this->type( "config__AdminPassword", " " );
+ parent::clickContinueButton();
+ $this->assertEquals( "Enter a password for the administrator account.",
+ $this->getText( LINK_DIV."div[2]/div[2]" ));
+
+
+ // Verify warning message for the blank 'Password again'
+ $this->type( "config_wgSitename", VALID_WIKI_NAME );
+ $this->type( "config__AdminPassword", VALID_PASSWORD );
+ $this->type( "config__AdminPassword2", " " );
+ parent::clickContinueButton();
+ $this->assertEquals( "The two passwords you entered do not match.",
+ $this->getText( LINK_DIV."div[2]/div[2]" ));
+
+
+ // Verify warning message for the different'Password' and 'Password again'
+ $this->type( "config_wgSitename", VALID_WIKI_NAME );
+ $this->type( "config__AdminPassword", VALID_PASSWORD );
+ $this->type( "config__AdminPassword2", INVALID_PASSWORD_AGAIN );
+ parent::clickContinueButton();
+ $this->assertEquals( "The two passwords you entered do not match.",
+ $this->getText( LINK_DIV."div[2]/div[2]" ));
+ }
+}
diff --git a/tests/selenium/installer/MediaWikiHelpFieldHintTestCase.php b/tests/selenium/installer/MediaWikiHelpFieldHintTestCase.php
new file mode 100644
index 00000000..355a2857
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiHelpFieldHintTestCase.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * MediaWikiHelpFieldHintTestCase
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+/*
+ * Test Case ID : 29 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name : Help field hint availability for the fields.
+ * Version : MediaWiki 1.18alpha
+*/
+
+require_once ( dirname( __FILE__ ) . '/MediaWikiInstallationCommonFunction.php' );
+
+class MediaWikiHelpFieldHintTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+
+ // Verify help field availability for the fields
+ public function testMySQLConnectToDatabaseFieldHint() {
+
+ parent::navigateConnetToDatabasePage();
+
+ // Verify help field for 'Database host'
+ $this->click( "//div[@id='DB_wrapper_mysql']/div/div[1]/div/span[1]" );
+ $this->assertEquals( MYSQL_DATABASE_HOST_HELP,
+ $this->getText( "//div[@id='DB_wrapper_mysql']/div/div[1]/div/span[2]" ) );
+
+ // Verify help field for 'Database name'
+ $this->click( "//div[@id='DB_wrapper_mysql']/fieldset[1]/div[1]/div[1]/div/span[1]" );
+ $this->assertEquals( MYSQL_DATABASE_NAME_HELP,
+ $this->getText( "//div[@id='DB_wrapper_mysql']/fieldset[1]/div[1]/div[1]/div/span[2]" ));
+
+
+ // Verify help field for 'Database table prefix'
+ $this->click("//div[@id='DB_wrapper_mysql']/fieldset[1]/div[2]/div[1]/div/span[1]" );
+ $this->assertEquals(MYSQL_DATABASE_TABLE_PREFIX_HELP,
+ $this->getText("//div[@id='DB_wrapper_mysql']/fieldset[1]/div[1]/div[1]/div/span[2]/p[1]" ));
+
+ // Verify help field for 'Database username'
+ $this->click( "//div[@id='DB_wrapper_mysql']/fieldset[2]/div[1]/div[1]/div/span[1]" );
+ $this->assertEquals( MYSQL_DATBASE_USERNAME_HELP,
+ $this->getText( "//div[@id='DB_wrapper_mysql']/fieldset[2]/div[1]/div[1]/div/span[2]" ));
+
+ // Verify help field for 'Database password'
+ $this->click( "//div[@id='DB_wrapper_mysql']/fieldset[2]/div[2]/div[1]/div/span[1]" );
+ $this->assertEquals( MYSQL_DATABASE_PASSWORD_HELP,
+ $this->getText( "//div[@id='DB_wrapper_mysql']/fieldset[2]/div[2]/div[1]/div/span[2]/p" ));
+ }
+
+
+ public function testSQLiteConnectToDatabaseFieldHint() {
+
+ parent::navigateConnetToDatabasePage();
+ $this->click( "DBType_sqlite" );
+
+ // Verify help field for 'SQLite data directory'
+ $this->click( "//div[@id='DB_wrapper_sqlite']/div[1]/div[1]/div/span[1]" );
+ $this->assertEquals( SQLITE_DATA_DIRECTORY_HELP,
+ $this->getText( "//div[@id='DB_wrapper_sqlite']/div[1]/div[1]/div/span[2]" ));
+
+ // Verify help field for 'Database name'
+ $this->click( "//div[@id='DB_wrapper_sqlite']/div[2]/div[1]/div/span[1]" );
+ $this->assertEquals( SQLITE_DATABASE_NAME_HELP , $this->getText( "//div[@id='DB_wrapper_sqlite']/div[2]/div[1]/div/span[2]/p" ));
+ }
+
+
+ public function testDatabaseSettingsFieldHint() {
+
+ $databaseName = DB_NAME_PREFIX."_db_field";
+ parent::navigateDatabaseSettingsPage($databaseName);
+
+ // Verify help field for 'Search engine'
+ $this->click( LINK_FORM."div[2]/span[1]" );
+ $this->assertEquals( SEARCH_ENGINE_HELP,
+ $this->getText( LINK_FORM."div[2]/span[2]" ));
+
+ // Verify help field for 'Database character set'
+ $this->click( LINK_FORM."div[4]/span[1]" );
+ $this->assertEquals( DATABASE_CHARACTER_SET_HELP,
+ $this->getText( LINK_FORM."div[4]/span[2]"));
+ parent::restartInstallation();
+ }
+
+
+ public function testNameFieldHint() {
+
+ $databaseName = DB_NAME_PREFIX."_name_field";
+ parent::navigateNamePage( $databaseName );
+
+ // Verify help field for 'Name of Wiki'
+ $this->click( LINK_FORM."div[1]/div[1]/div/span[1]" );
+ $this->assertEquals( NAME_OF_WIKI_HELP,
+ $this->getText( LINK_FORM."div[1]/div[1]/div/span[2]/p" ));
+
+ // Verify help field for 'Project namespace'
+ $this->click( LINK_FORM."div[2]/div[1]/div/span[1]" );
+ $this->assertEquals( PROJECT_NAMESPACE_HELP,
+ $this->getText( LINK_FORM."div[2]/div[1]/div/span[2]/p"));
+
+ // Verify help field for 'Your Name'
+ $this->click( LINK_FORM."fieldset/div[1]/div[1]/div/span[1]" );
+ $this->assertEquals( USER_NAME_HELP,
+ $this->getText( LINK_FORM."fieldset/div[1]/div[1]/div/span[2]/p" ));
+
+ // Verify help field for 'E mail address'
+ $this->click( LINK_FORM."fieldset/div[4]/div[1]/div/span[1]" );
+ $this->assertEquals( EMAIL_ADDRESS_HELP,
+ $this->getText( LINK_FORM."fieldset/div[4]/div[1]/div/span[2]/p" ));
+
+ parent::restartInstallation();
+ }
+}
+
diff --git a/tests/selenium/installer/MediaWikiInstallationCommonFunction.php b/tests/selenium/installer/MediaWikiInstallationCommonFunction.php
new file mode 100644
index 00000000..99df8a2a
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiInstallationCommonFunction.php
@@ -0,0 +1,283 @@
+<?php
+/**
+ * MediaWikiInstallationCommonFunction
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+require_once 'PHPUnit/Extensions/SeleniumTestCase.php';
+require_once ( dirname( __FILE__ ) . '/MediaWikiInstallationConfig.php' );
+require_once ( dirname(__FILE__) . '/MediaWikiInstallationMessage.php' );
+require_once ( dirname(__FILE__) . '/MediaWikiInstallationVariables.php');
+
+
+class MediaWikiInstallationCommonFunction extends PHPUnit_Extensions_SeleniumTestCase {
+
+ function setUp() {
+ $this->setBrowser( TEST_BROWSER );
+ $this->setBrowserUrl("http://".HOST_NAME.":".PORT."/".DIRECTORY_NAME."/");
+ }
+
+
+ public function navigateInitialpage() {
+ $this->open( "http://".HOST_NAME.":".PORT."/".DIRECTORY_NAME."/" );
+ }
+
+
+ // Navigate to the 'Language' page
+ public function navigateLanguagePage() {
+ $this->open( "http://".HOST_NAME.":".PORT."/".DIRECTORY_NAME."/config/index.php" );
+ }
+
+
+ // Navigate to the 'Welcome to MediaWiki' page
+ public function navigateWelcometoMediaWikiPage() {
+ $this->open( "http://".HOST_NAME.":".PORT."/".DIRECTORY_NAME."/config/index.php" );
+ $this->click( "submit-continue ");
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ }
+
+
+ // Navigate yo 'Connect to Database' page
+ public function navigateConnetToDatabasePage() {
+ $this->open( "http://".HOST_NAME.":".PORT."/".DIRECTORY_NAME."/config/index.php" );
+
+ // 'Welcome to MediaWiki!' page
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // 'Connect to Database' page
+ $this->click("submit-continue");
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ }
+
+
+ // Navigate to the 'Database Settings' page
+ public function navigateDatabaseSettingsPage( $databaseName ) {
+
+ $this->open( "http://".HOST_NAME.":".PORT."/".DIRECTORY_NAME."/config/index.php" );
+
+ // 'Welcome to MediaWiki!' page
+ $this->click("submit-continue");
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // 'Connect to Database' page
+ $this->click("submit-continue");
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ $this->type("mysql_wgDBname", $databaseName );
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ }
+
+
+ // Navigate to the 'Name' page
+ public function navigateNamePage( $databaseName ) {
+ $this->open( "http://".HOST_NAME.":".PORT."/".DIRECTORY_NAME."/config/index.php" );
+
+ // 'Welcome to MediaWiki!' page
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // 'Connect to Database' page
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ $this->type( "mysql_wgDBname", $databaseName );
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // Database settings
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ }
+
+
+ // Navigate 'Options' page
+ public function navigateOptionsPage( $databaseName ) {
+
+ $this->open( "http://".HOST_NAME.":".PORT."/".DIRECTORY_NAME."/config/index.php" );
+
+ // 'Welcome to MediaWiki!' page
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // 'Connect to Database' page
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ $this->type( "mysql_wgDBname", $databaseName );
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // Database settings
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // Name
+ $this->type( "config_wgSitename", NAME_OF_WIKI );
+ $this->type( "config__AdminName", ADMIN_USER_NAME);
+ $this->type( "config__AdminPassword", ADMIN_PASSWORD );
+ $this->type( "config__AdminPassword2", ADMIN_RETYPE_PASSWORD );
+ $this->type( "config__AdminEmail", ADMIN_EMAIL_ADDRESS );
+
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ }
+
+
+ // Navigate 'Install' page
+ public function navigateInstallPage( $databaseName ) {
+
+ $this->open( "http://".HOST_NAME.":".PORT."/".DIRECTORY_NAME."/config/index.php" );
+
+ // 'Welcome to MediaWiki!' page
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // 'Connect to Database' page
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ $this->type( "mysql_wgDBname", $databaseName );
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // Database settings
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // Name
+ $this->type( "config_wgSitename", NAME_OF_WIKI );
+ $this->type( "config__AdminName", ADMIN_USER_NAME);
+ $this->type( "config__AdminPassword", ADMIN_PASSWORD );
+ $this->type( "config__AdminPassword2", ADMIN_RETYPE_PASSWORD );
+ $this->type( "config__AdminEmail", ADMIN_EMAIL_ADDRESS );
+
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // Options page
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ }
+
+
+ // Navigate to 'Complete' page
+ public function navigateCompletePage( $databaseName ) {
+ $this->open( "http://".HOST_NAME.":".PORT."/".DIRECTORY_NAME."/config/index.php" );
+
+ // 'Welcome to MediaWiki!' page
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // 'Connect to Database' page
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ $this->type( "mysql_wgDBname", $databaseName );
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // Database settings
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // Name
+ $this->type( "config_wgSitename", NAME_OF_WIKI );
+ $this->type( "config__AdminName", ADMIN_USER_NAME);
+ $this->type( "config__AdminPassword", ADMIN_PASSWORD );
+ $this->type( "config__AdminPassword2", ADMIN_RETYPE_PASSWORD );
+ $this->type( "config__AdminEmail", ADMIN_EMAIL_ADDRESS );
+
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // Options page
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // Install page
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ $this->chooseCancelOnNextConfirmation();
+ }
+
+
+ // Complete the Name page fields
+ public function completeNamePage() {
+ $this->type( "config_wgSitename", NAME_OF_WIKI );
+ $this->type( "config__AdminName", ADMIN_USER_NAME);
+ $this->type( "config__AdminPassword", ADMIN_PASSWORD );
+ $this->type( "config__AdminPassword2", ADMIN_RETYPE_PASSWORD );
+ $this->type( "config__AdminEmail", ADMIN_EMAIL_ADDRESS );
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME);
+ }
+
+
+ // Clicking on the 'Continue' button in any MediaWiki page
+ public function clickContinueButton() {
+ $this->click( "submit-continue" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ }
+
+
+ // Clicking on the 'Back' button in any MediaWiki page
+ public function clickBackButton() {
+ $this->click( "submit-back" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ }
+
+
+ // Restarting the installation
+ public function restartInstallation() {
+ $this->click( "link=Restart installation" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ $this->click( "submit-restart" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ }
+
+
+ // Verify 'MediaWiki' logo available in the initial screen
+ public function mediaWikiLogoPresentInitialScreen() {
+ $this->assertTrue( $this->isElementPresent( "//img[@alt='The MediaWiki logo']" ));
+ }
+
+
+ // Verify 'MediaWiki' logo available
+ public function mediaWikiLogoPresent() {
+ $this->assertTrue( $this->isElementPresent( "//div[@id='p-logo']/a" ));
+ }
+
+
+ public function completePageSuccessfull() {
+ $this->assertEquals( "Complete!",
+ $this->getText( "//div[@id='bodyContent']/div/div/h2" ));
+
+ // 'Congratulations!' text should be available in the 'Complete!' page.
+ $this->assertEquals( "Congratulations!",
+ $this->getText( "//div[@id='bodyContent']/div/div/div[2]/form/div[1]/div[2]/p[1]/b" ));
+ }
+}
diff --git a/tests/selenium/installer/MediaWikiInstallationConfig.php b/tests/selenium/installer/MediaWikiInstallationConfig.php
new file mode 100644
index 00000000..d3067d69
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiInstallationConfig.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * MediaWikiInstallationConfig
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+
+/*
+ * MediaWikiInstallerTestSuite.php can be run one time successfully
+ * with current value of the 'DB_NAME_PREFIX'.
+ * If you wish to run the suite more than one time, you need to change
+ * the value of the 'DB_NAME_PREFIX'.
+*/
+define('DB_NAME_PREFIX', "database_name" );
+define('DIRECTORY_NAME', "mediawiki" );
+define( 'PORT', "8080" );
+define( 'HOST_NAME', "localhost" );
+
+/*
+ * Use the followings to run the test suite in different browsers.
+ * Firefox : *firefox
+ * IE : *iexplore
+ * Google chrome : *googlechrome
+ * Opera : *opera
+*/
+define ( 'TEST_BROWSER', "*firefox" );
diff --git a/tests/selenium/installer/MediaWikiInstallationMessage.php b/tests/selenium/installer/MediaWikiInstallationMessage.php
new file mode 100644
index 00000000..a348b542
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiInstallationMessage.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * MediaWikiInstallationConfig
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+
+// 'MySQL' database type help field hint
+define( 'MYSQL_DATABASE_HOST_HELP', "If your database server is on different server, enter the host name or IP address here. \nIf you are using shared web hosting, your hosting provider should give you the correct host name in their documentation. \nIf you are installing on a Windows server and using MySQL, using \"localhost\" may not work for the server name. If it does not, try \"127.0.0.1\" for the local IP address." );
+define( 'MYSQL_DATABASE_NAME_HELP', "Choose a name that identifies your wiki. It should not contain spaces or hyphens. \nIf you are using shared web hosting, your hosting provider will either give you a specific database name to use or let you create databases via a control panel." );
+define( 'MYSQL_DATABASE_TABLE_PREFIX_HELP', "Choose a name that identifies your wiki. It should not contain spaces or hyphens.");
+define( 'MYSQL_DATBASE_USERNAME_HELP', "Enter the username that will be used to connect to the database during the installation process. This is not the username of the MediaWiki account; this is the username for your database." );
+define( 'MYSQL_DATABASE_PASSWORD_HELP', "Enter the password that will be used to connect to the database during the installation process. This is not the password for the MediaWiki account; this is the password for your database." );
+
+
+// 'SQLite' database type help field hint
+define( 'SQLITE_DATA_DIRECTORY_HELP', "SQLite stores all data in a single file. \nThe directory you provide must be writable by the webserver during installation. \nIt should not be accessible via the web, this is why we're not putting it where your PHP files are. \nThe installer will write a .htaccess file along with it, but if that fails someone can gain access to your raw database. That includes raw user data (e-mail addresses, hashed passwords) as well as deleted revisions and other restricted data on the wiki. \nConsider putting the database somewhere else altogether, for example in /var/lib/mediawiki/yourwiki." );
+define( 'SQLITE_DATABASE_NAME_HELP', "Choose a name that identifies your wiki. Do not use spaces or hyphens. This will be used for the SQLite data file name.");
+
+
+// 'Database settings' page hel0p field hint
+define( 'SEARCH_ENGINE_HELP', "InnoDB is almost always the best option, since it has good concurrency support. \nMyISAM may be faster in single-user or read-only installations. MyISAM databases tend to get corrupted more often than InnoDB databases." );
+define( 'DATABASE_CHARACTER_SET_HELP', "In binary mode, MediaWiki stores UTF-8 text to the database in binary fields. This is more efficient than MySQL's UTF-8 mode, and allows you to use the full range of Unicode characters. \nIn UTF-8 mode, MySQL will know what character set your data is in, and can present and convert it appropriately, but it will not let you store characters above the Basic Multilingual Plane." );
+
+
+// 'Name' page help field hint
+define( 'NAME_OF_WIKI_HELP', "This will appear in the title bar of the browser and in various other places.");
+define( 'PROJECT_NAMESPACE_HELP', "Following Wikipedia's example, many wikis keep their policy pages separate from their content pages, in a \"project namespace\". All page titles in this namespace start with a certain prefix, which you can specify here. Traditionally, this prefix is derived from the name of the wiki, but it cannot contain punctuation characters such as \"#\" or \":\"." );
+define( 'USER_NAME_HELP', "Enter your preferred username here, for example \"Joe Bloggs\". This is the name you will use to log in to the wiki." );
+define( 'EMAIL_ADDRESS_HELP', "Enter an e-mail address here to allow you to receive e-mail from other users on the wiki, reset your password, and be notified of changes to pages on your watchlist." );
+define( 'SUBSCRIBE_MAILING_LIST_HELP', "This is a low-volume mailing list used for release announcements, including important security announcements. You should subscribe to it and update your MediaWiki installation when new versions come out." );
+
+
+
diff --git a/tests/selenium/installer/MediaWikiInstallationVariables.php b/tests/selenium/installer/MediaWikiInstallationVariables.php
new file mode 100644
index 00000000..bb11d022
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiInstallationVariables.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * MediaWikiInstallationConfig
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+
+// Common variables
+define('PAGE_LOAD_TIME', "80000" );
+
+// Common links
+define( 'LINK_DIV', "//div[@id='bodyContent']/div/div/");
+define( 'LINK_FORM', "//div[@id='bodyContent']/div/div/div[2]/form/" );
+define( 'LINK_RIGHT_FRAMEWORK', "//div[@id='bodyContent']/div/div/div[1]/ul[1]/");
+
+// 'Name' page input values
+define( 'NAME_OF_WIKI', "Site Name" );
+define( 'ADMIN_USER_NAME', "My Name" );
+define( 'ADMIN_PASSWORD', "12345" );
+define ( 'ADMIN_RETYPE_PASSWORD', "12345" );
+define ( 'ADMIN_EMAIL_ADDRESS', "admin@example.com" );
+
+
+// 'Name' page input values for warning messages
+define( 'VALID_WIKI_NAME', "MyWiki" );
+define( 'VALID_YOUR_NAME', "FirstName LastName" );
+define( 'VALID_PASSWORD', "12345" );
+define( 'VALID_PASSWORD_AGAIN', "12345" );
+define( 'INVALID_PASSWORD_AGAIN', "123" );
+define( 'VALID_NAMESPACE', "Mynamespace" );
+define( 'INVALID_NAMESPACE', "##..##" );
+
+
+// 'Database settings' page input values
+define( 'DB_WEB_USER', "different" );
+define('DB_WEB_USER_PASSWORD', "12345" );
+
+
+// 'Connet to database' page input values
+define( 'DATABASE_PREFIX',"databaseprefix" );
+
+
+// 'Connet to database' page input values for warning messages
+define( 'VALID_DB_HOST', "localhost" );
+define( 'INVALID_DB_HOST', "local" );
+define( 'INVALID_DB_NAME', "my-wiki" );
+define( 'VALID_DB_NAME', "my_wiki1");
+define( 'INVALID_DB_PREFIX', "database prefix" );
+define( 'VALID_DB_PREFIX', "database_prefix");
+define( 'INVALID_DB_USER_NAME', "roots" );
+define( 'VALID_DB_USER_NAME', "root");
+define( 'INVALID_DB_PASSWORD', "12345" );
+
+
diff --git a/tests/selenium/installer/MediaWikiInstallerTestSuite.php b/tests/selenium/installer/MediaWikiInstallerTestSuite.php
new file mode 100644
index 00000000..386a50ea
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiInstallerTestSuite.php
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * MediaWikiInstallerTestSuite
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+require_once 'PHPUnit/Framework.php';
+require_once 'PHPUnit/Framework/TestSuite.php';
+
+require_once ( dirname( __FILE__ ) . '/MediaWikiUserInterfaceTestCase.php' );
+require_once ( dirname( __FILE__ ) . '/MediaWikiButtonsAvailabilityTestCase.php' );
+require_once ( dirname( __FILE__ ) . '/MediaWikiHelpFieldHintTestCase.php' );
+require_once ( dirname( __FILE__ ) . '/MediaWikiRightFrameworkLinksTestCase.php' );
+require_once ( dirname( __FILE__ ) . '/MediaWikiRestartInstallationTestCase.php' );
+require_once ( dirname( __FILE__ ) . '/MediaWikiErrorsConnectToDatabasePageTestCase.php' );
+require_once ( dirname( __FILE__ ) . '/MediaWikiErrorsNamepageTestCase.php' );
+require_once ( dirname( __FILE__ ) . '/MediaWikiMySQLDataBaseTestCase.php' );
+require_once ( dirname( __FILE__ ) . '/MediaWikiMySQLiteDataBaseTestCase.php' );
+require_once ( dirname( __FILE__ ) . '/MediaWikiUpgradeExistingDatabaseTestCase.php' );
+require_once ( dirname( __FILE__ ) . '/MediaWikiDifferntDatabasePrefixTestCase.php' );
+require_once ( dirname( __FILE__ ) . '/MediaWikiDifferentDatabaseAccountTestCase.php' );
+require_once ( dirname( __FILE__ ) . '/MediaWikiOnAlreadyInstalledTestCase.php' );
+
+
+
+
+$suite = new PHPUnit_Framework_TestSuite('ArrayTest');
+$result = new PHPUnit_Framework_TestResult;
+
+$suite->run($result);
diff --git a/tests/selenium/installer/MediaWikiMySQLDataBaseTestCase.php b/tests/selenium/installer/MediaWikiMySQLDataBaseTestCase.php
new file mode 100644
index 00000000..abf9ddf2
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiMySQLDataBaseTestCase.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * MediaWikiOnAlreadyInstalledTestCase
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+
+require_once (dirname(__FILE__).'/'.'MediaWikiInstallationCommonFunction.php');
+
+/*
+ * Test Case ID : 01 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name : Install Mediawiki using 'MySQL' database type successfully
+ * Version : MediaWiki 1.18alpha
+*/
+
+class MediaWikiMySQLDataBaseTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+ // Verify MediaWiki installation using 'MySQL' database type
+ public function testMySQLDatabaseSuccess() {
+
+ $databaseName = DB_NAME_PREFIX."_sql_db";
+
+ parent::navigateConnetToDatabasePage();
+
+ // Verify 'MySQL" is selected as the default database type
+ $this->assertEquals( "MySQL settings", $this->getText( "//div[@id='DB_wrapper_mysql']/h3" ));
+
+ // Change 'Database name'
+ $defaultDbName = $this->getText( "mysql_wgDBname" );
+ $this->type( "mysql_wgDBname", " ");
+ $this->type( "mysql_wgDBname", $databaseName );
+ $this->assertNotEquals( $defaultDbName, $databaseName );
+
+ // 'Database settings' page
+ parent::clickContinueButton();
+
+ // 'Name' page
+ parent::clickContinueButton();
+ parent::completeNamePage();
+
+ // 'Options page
+ parent::clickContinueButton();
+
+ // 'Install' page
+ parent::clickContinueButton();
+
+ // 'Complete' page
+ parent::completePageSuccessfull();
+ parent::restartInstallation();
+ }
+}
diff --git a/tests/selenium/installer/MediaWikiMySQLiteDataBaseTestCase.php b/tests/selenium/installer/MediaWikiMySQLiteDataBaseTestCase.php
new file mode 100644
index 00000000..fe704a42
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiMySQLiteDataBaseTestCase.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * MediaWikiMySQLiteataBaseTestCase
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+
+require_once (dirname(__FILE__).'/'.'MediaWikiInstallationCommonFunction.php');
+
+/*
+ * Test Case ID : 06 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name : Install Mediawiki using 'MySQL' database type successfully
+ * Version : MediaWiki 1.18alpha
+*/
+
+class MediaWikiMySQLiteDataBaseTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+ // Verify MediaWiki installation using 'MySQL' database type
+ public function testMySQLDatabaseSuccess() {
+
+ $databaseName = DB_NAME_PREFIX."_sqlite_db";
+
+ parent::navigateConnetToDatabasePage();
+ $this->click( "DBType_sqlite" );
+
+ // Select 'SQLite' database type
+ $this->assertEquals( "SQLite settings", $this->getText( "//div[@id='DB_wrapper_sqlite']/h3" ));
+
+ // Change database name
+ $defaultDbName = $this->getText( "sqlite_wgDBname" );
+ $this->type( "sqlite_wgDBname", " ");
+ $this->type( "sqlite_wgDBname", $databaseName );
+ $this->assertNotEquals( $defaultDbName, $databaseName );
+
+ // 'Database settings' page
+ parent::clickContinueButton();
+
+ // 'Name' page
+ parent::clickContinueButton();
+ parent::completeNamePage();
+
+ // 'Options page
+ parent::clickContinueButton();
+
+ // 'Install' page
+ parent::clickContinueButton();
+
+ // 'Complete' page
+ parent::completePageSuccessfull();
+ parent::restartInstallation();
+ }
+}
diff --git a/tests/selenium/installer/MediaWikiOnAlreadyInstalledTestCase.php b/tests/selenium/installer/MediaWikiOnAlreadyInstalledTestCase.php
new file mode 100644
index 00000000..e8b8f9b0
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiOnAlreadyInstalledTestCase.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+
+require_once (dirname(__FILE__).'/'.'MediaWikiInstallationCommonFunction.php');
+
+
+/*
+ * Test Case ID : 03 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name : Install mediawiki on a already installed Mediawiki.]
+ * Version : MediaWiki 1.18alpha
+*/
+
+class MediaWikiOnAlreadyInstalledTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+ // Install Mediawiki using 'MySQL' database type.
+ public function testInstallOnAlreadyInstalled() {
+
+ $databaseName = DB_NAME_PREFIX."_already_installed";
+ parent::navigateInstallPage( $databaseName );
+
+ // 'Options' page
+ parent::clickBackButton();
+
+ // Install page
+ parent::clickContinueButton();
+
+ // 'Install' page should display after the 'Option' page
+ $this->assertEquals( "Install", $this->getText( LINK_DIV."h2" ));
+
+ // Verify warning text displayed
+ $this->assertEquals( "Warning: You seem to have already installed MediaWiki and are trying to install it again. Please proceed to the next page.",
+ $this->getText( LINK_FORM."div[1]/div[2]" ));
+
+ // Complete page
+ parent::clickContinueButton();
+ parent::completePageSuccessfull();
+ $this->chooseCancelOnNextConfirmation();
+ parent::restartInstallation();
+ }
+}
diff --git a/tests/selenium/installer/MediaWikiRestartInstallationTestCase.php b/tests/selenium/installer/MediaWikiRestartInstallationTestCase.php
new file mode 100644
index 00000000..b0a38000
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiRestartInstallationTestCase.php
@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * MediaWikiRestartInstallationTestCase
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+
+
+require_once (dirname(__FILE__).'/'.'MediaWikiInstallationCommonFunction.php');
+
+/*
+ * Test Case ID : 11, 12 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name : Install mediawiki on a already installed Mediawiki.
+ * Version : MediaWiki 1.18alpha
+*/
+
+class MediaWikiRestartInstallationTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+ // Verify restarting the installation
+ public function testSuccessRestartInstallation() {
+
+ $dbNameBeforeRestart = DB_NAME_PREFIX."_db_before";
+ parent::navigateDatabaseSettingsPage( $dbNameBeforeRestart );
+
+ // Verify 'Restart installation' link available
+ $this->assertTrue($this->isElementPresent( "link=Restart installation" ));
+
+ // Click 'Restart installation'
+ $this->click( "link=Restart installation ");
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // 'Restart Installation' page displayed
+ $this->assertEquals( "Restart installation", $this->getText( LINK_DIV."h2"));
+
+ // Restart warning message displayed
+ $this->assertTrue($this->isTextPresent( "exact:Do you want to clear all saved data that you have entered and restart the installation process?" ));
+
+ // Click on the 'Yes, restart' button
+ $this->click( "submit-restart" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // Navigate to the initial installation page(Language).
+ $this->assertEquals( "Language", $this->getText( LINK_DIV."h2" ));
+
+ // 'Welcome to MediaWiki!' page
+ parent::clickContinueButton();
+
+ // 'Connect to database' page
+ parent::clickContinueButton();
+
+ // saved data should be deleted
+ $dbNameAfterRestart = $this->getValue("mysql_wgDBname");
+ $this->assertNotEquals($dbNameBeforeRestart, $dbNameAfterRestart);
+ }
+
+
+ // Verify cancelling restart
+ public function testCancelRestartInstallation() {
+
+ $dbNameBeforeRestart = DB_NAME_PREFIX."_cancel_restart";
+
+ parent::navigateDatabaseSettingsPage( $dbNameBeforeRestart);
+ // Verify 'Restart installation' link available
+ $this->assertTrue($this->isElementPresent( "link=Restart installation" ));
+
+ $this->click( "link=Restart installation" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // 'Restart Installation' page displayed
+ $this->assertEquals( "Restart installation", $this->getText( LINK_DIV."h2" ));
+
+ // Restart warning message displayed
+ $this->assertTrue( $this->isTextPresent( "Do you want to clear all saved data that you have entered and restart the installation process?"));
+
+ // Click on the 'Back' button
+ parent::clickBackButton();
+
+ // Navigates to the previous page
+ $this->assertEquals( "Database settings", $this->getText( LINK_DIV."h2" ));
+
+ // 'Connect to database' page
+ parent::clickBackButton();
+
+ // Saved data remain on the page.
+ $dbNameAfterRestart = $this->getValue( "mysql_wgDBname" );
+ $this->assertEquals( $dbNameBeforeRestart, $dbNameAfterRestart );
+ }
+}
diff --git a/tests/selenium/installer/MediaWikiRightFrameworkLinksTestCase.php b/tests/selenium/installer/MediaWikiRightFrameworkLinksTestCase.php
new file mode 100644
index 00000000..346f24f8
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiRightFrameworkLinksTestCase.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * MediaWikiRightFrameworkLinksTestCase
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+
+require_once (dirname(__FILE__).'/'.'MediaWikiInstallationCommonFunction.php');
+
+/*
+ * Test Case ID : 14, 15, 16, 17 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name : User selects 'Read me' link.
+ * User selects 'Release notes' link.
+ * User selects 'Copying' link.
+ * User selects 'Upgrading' link.
+ * Version : MediaWiki 1.18alpha
+*/
+
+
+class MediaWikiRightFrameworkLinksTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+ public function testLinksAvailability() {
+
+ $this->open( "http://".HOST_NAME.":".PORT."/".DIRECTORY_NAME."/config/index.php" );
+
+ // Verify 'Read me' link availability
+ $this->assertTrue($this->isElementPresent( "link=Read me" ));
+
+ // Verify 'Release notes' link availability
+ $this->assertTrue($this->isElementPresent( "link=Release notes" ));
+
+ // Verify 'Copying' link availability
+ $this->assertTrue($this->isElementPresent( "link=Copying" ));
+ }
+
+ public function testPageNavigation() {
+
+ $this->open( "http://".HOST_NAME.":".PORT."/".DIRECTORY_NAME."/config/index.php" );
+
+ // Navigate to the 'Read me' page
+ $this->click( "link=Read me" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ $this->assertEquals( "Read me", $this->getText( LINK_DIV."h2[1]" ));
+ $this->assertTrue($this->isElementPresent( "submit-back" ));
+ parent::clickBackButton();
+
+ // Navigate to the 'Release notes' page
+ $this->click( "link=Release notes" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME);
+ $this->assertEquals( "Release notes", $this->getText( LINK_DIV."h2[1]" ));
+ $this->assertTrue( $this->isElementPresent( "submit-back" ));
+ parent::clickBackButton();
+
+ // Navigate to the 'Copying' page
+ $this->click( "link=Copying" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ $this->assertEquals( "Copying", $this->getText( LINK_DIV."h2[1]" ));
+ $this->assertTrue($this->isElementPresent( "submit-back" ));
+ parent::clickBackButton();
+
+ // Navigate to the 'Upgrading' page
+ $this->click( "link=Upgrading" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ $this->assertEquals( "Upgrading", $this->getText( LINK_DIV."h2[1]" ));
+ }
+}
diff --git a/tests/selenium/installer/MediaWikiUpgradeExistingDatabaseTestCase.php b/tests/selenium/installer/MediaWikiUpgradeExistingDatabaseTestCase.php
new file mode 100644
index 00000000..0ab5e659
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiUpgradeExistingDatabaseTestCase.php
@@ -0,0 +1,117 @@
+<?php
+
+/**
+ * MediaWikiUpgradeExistingDatabaseTestCase
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+
+require_once (dirname(__FILE__).'/'.'MediaWikiInstallationCommonFunction.php');
+
+/*
+ * Test Case ID : 05 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name : Install Mediawiki by updating the existing database.
+ * Version : MediaWiki 1.18alpha
+*/
+
+
+class MediaWikiUpgradeExistingDatabaseTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+ // Install Mediawiki using 'MySQL' database type.
+ public function testUpgradeExistingDatabase() {
+
+ $databaseName = DB_NAME_PREFIX."_upgrade_existing";
+ parent::navigateInstallPage( $databaseName );
+
+ $this->open( "http://localhost:".PORT."/".DIRECTORY_NAME."/config/index.php" );
+ $this->assertEquals( "Install", $this->getText( LINK_DIV."h2" ));
+ $this->assertEquals( "Warning: You seem to have already installed MediaWiki and are trying to install it again. Please proceed to the next page.",
+ $this->getText( LINK_DIV."div[2]/form/div[1]/div[2]" ));
+
+ // 'Optionis' page
+ parent::clickBackButton();
+
+ // 'Name' page
+ parent::clickBackButton();
+
+ // 'Database settings' page
+ parent::clickBackButton();
+
+ // 'Connect to database' page
+ parent::clickBackButton();
+ $this->type( "mysql_wgDBname", $databaseName );
+ parent::clickContinueButton();
+
+ // 'Upgrade existing installation' page displayed next to the 'Connect to database' page.
+ $this->assertEquals( "Upgrade existing installation", $this->getText( LINK_DIV."h2" ));
+
+ // Warning message displayed.
+ $this->assertEquals( "There are MediaWiki tables in this database. To upgrade them to MediaWiki 1.18alpha, click Continue.",
+ $this->getText( LINK_DIV."div[2]/form/div[1]/div[2]" ));
+
+ parent::clickContinueButton();
+ $this->assertEquals( "Upgrade existing installation",
+ $this->getText( LINK_DIV."h2" ));
+
+ // 'Upgrade complete.' text display
+ $this->assertEquals("Upgrade complete.",
+ $this->getText("//div[@id='bodyContent']/div/div[1]/div[4]/form/div[1]/div[2]/p[1]"));
+
+ $this->assertEquals("You can now Folder/index.php start using your wiki.",
+ $this->getText("//div[@id='bodyContent']/div/div[1]/div[4]/form/div[1]/div[2]/p[2]" ));
+
+ $this->assertEquals( "Folder/index.php start using your wiki",
+ $this->getText( "link=Folder/index.php start using your wiki" ));
+
+ $this->assertTrue($this->isElementPresent( "submit-regenerate" ));
+ $this->click( "submit-regenerate" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+ $this->assertEquals( "Database settings",
+ $this->getText( LINK_DIV."h2" ));
+
+ // 'Database settings' page
+ parent::clickContinueButton();
+
+ // Name page
+ parent::completeNamePage();
+
+ // Options page
+ parent::clickContinueButton();
+
+ // Install page
+ $this->assertEquals( "Warning: You seem to have already installed MediaWiki and are trying to install it again. Please proceed to the next page.",
+ $this->getText( LINK_FORM."div[1]/div[2]" ));
+ parent::clickContinueButton();
+
+ // complete
+ parent::completePageSuccessfull();
+ $this->chooseCancelOnNextConfirmation();
+ parent::restartInstallation();
+ }
+} \ No newline at end of file
diff --git a/tests/selenium/installer/MediaWikiUserInterfaceTestCase.php b/tests/selenium/installer/MediaWikiUserInterfaceTestCase.php
new file mode 100644
index 00000000..7be39c04
--- /dev/null
+++ b/tests/selenium/installer/MediaWikiUserInterfaceTestCase.php
@@ -0,0 +1,531 @@
+<?php
+
+/**
+ * MediaWikiUserInterfaceTestCase
+ *
+ * @file
+ * @ingroup Maintenance
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Maintenance
+ *
+ */
+
+require_once (dirname(__FILE__).'/'.'MediaWikiInstallationCommonFunction.php');
+
+/*
+ * Test Case ID : 18 - 27 (http://www.mediawiki.org/wiki/New_installer/Test_plan)
+ * Test Case Name : UI of MediaWiki initial/ Language/ Welcome to MediaWiki!/ Connect to database/
+ * Database settings/ Name/ Options/ Install/ Complete/ Restart Inslation pages
+ * Version : MediaWiki 1.18alpha
+*/
+
+
+class MediaWikiUserInterfaceTestCase extends MediaWikiInstallationCommonFunction {
+
+ function setUp() {
+ parent::setUp();
+ }
+
+
+ public function testInitialPageUI() {
+
+ parent::navigateInitialpage();
+
+ // MediaWiki logo available
+ $this->assertTrue( $this->isElementPresent( "//img[@alt='The MediaWiki logo']" ));
+
+ // 'MediaWiki 1.18alpha' text available
+ $this->assertEquals( "MediaWiki 1.18alpha", $this->getText( "//h1" ));
+
+ // 'LocalSettings.php not found.' text available
+ $this->assertEquals( "LocalSettings.php not found.", $this->getText( "//p[1]" ));
+
+ // 'Please set up the wiki first' text available
+ $this->assertEquals( "Please set up the wiki first.", $this->getText( "//p[2]" ));
+
+ // 'set up the wiki' link available
+ $this->assertTrue($this->isElementPresent( "link=set up the wiki" ));
+ }
+
+
+ public function testlanguagePageUI() {
+
+ parent::navigateLanguagePage();
+
+ // Verify 'Language' heading
+ $this->assertEquals( "Language", $this->getText( LINK_DIV."h2" ));
+
+ // 'Your language' label available
+ $this->assertEquals( "Your language:",
+ $this->getText( LINK_FORM."div[1]/div[1]/label" ));
+
+ // 'Your language' dropdown available
+ $this->assertTrue( $this->isElementPresent( "UserLang" ));
+
+ // 'Wiki language' label available
+ $this->assertEquals( "Wiki language:",
+ $this->getText( LINK_FORM."div[2]/div[1]/label" ));
+
+ // 'Wiki language' dropdown available
+ $this->assertTrue($this->isElementPresent( "ContLang" ));
+ }
+
+
+ public function testWelcometoMediaWikiUI() {
+
+ parent::navigateWelcometoMediaWikiPage();
+
+ // Verify 'Welcome to MediaWiki!' heading
+ $this->assertEquals( "Welcome to MediaWiki!",
+ $this->getText( LINK_DIV."h2" ));
+
+ // Verify environment ok text displayed.
+ $this->assertEquals( "The environment has been checked.You can install MediaWiki.",
+ $this->getText( LINK_DIV."div[6]/span" ));
+ }
+
+
+ public function testConnectToDatabaseUI() {
+
+ parent::navigateConnetToDatabasePage();
+
+ // 'MYSQL radio button available
+ $this->assertEquals( "MySQL",
+ $this->getText( LINK_FORM."div[2]/div[2]/ul/li[1]/label" ));
+ $this->assertTrue( $this->isElementPresent( LINK_FORM."div[2]/div[2]/ul/li[1]" ));
+
+ // 'SQLite' radio button available
+ $this->assertTrue( $this->isElementPresent( LINK_FORM."div[2]/div[2]/ul/li[2]" ));
+ $this->assertEquals( "SQLite", $this->getText( LINK_FORM."div[2]/div[2]/ul/li[2]/label "));
+
+ // 'Database host' label available
+ $this->assertEquals( "Database host:", $this->getText( "//div[@id='DB_wrapper_mysql']/div/div[1]/label" ));
+
+ // 'Database host' text box default to 'localhost'
+ $this->assertEquals( "localhost", $this->getValue( "mysql_wgDBserver" ));
+
+ // 'Identify this wiki' section available
+ $this->assertTrue( $this->isElementPresent( "//div[@id='DB_wrapper_mysql']/fieldset[1]/legend" ));
+
+ // 'Identify this wiki' label available
+ $this->assertEquals( "Identify this wiki", $this->getText( "//div[@id='DB_wrapper_mysql']/fieldset[1]/legend" ));
+
+ // 'Database name' lable available
+ $this->assertEquals( "Database name:",
+ $this->getText( "//div[@id='DB_wrapper_mysql']/fieldset[1]/div[1]/div[1]/label" ));
+
+ // Verify 'Database name:' text box is default to 'my_wiki'
+ $this->assertEquals( "my_wiki", $this->getValue( "mysql_wgDBname" ));
+
+ // Verify 'Database table prefix:' label available
+ $this->assertEquals( "Database table prefix:",
+ $this->getText( "//div[@id='DB_wrapper_mysql']/fieldset[1]/div[2]/div[1]/label" ));
+
+ // 'User account for installation' section available
+ $this->assertTrue( $this->isElementPresent( "//div[@id='DB_wrapper_mysql']/fieldset[2]/legend" ));
+
+ // 'User account for installation' label available
+ $this->assertEquals( "User account for installation", $this->getText( "//div[@id='DB_wrapper_mysql']/fieldset[2]/legend" ));
+
+ // 'Database username' label available
+ $this->assertEquals( "Database username:",
+ $this->getText( "//div[@id='DB_wrapper_mysql']/fieldset[2]/div[1]/div[1]/label" ));
+
+ // 'Database username' text box defaults to 'root'
+ $this->assertEquals("root", $this->getValue( "mysql__InstallUser" ));
+
+ // 'Database password' label available
+ $this->assertEquals( "Database password:",
+ $this->getText( "//div[@id='DB_wrapper_mysql']/fieldset[2]/div[2]/div[1]/label" ));
+ }
+
+
+
+ public function testDatabaseSettingsUI() {
+
+ $databaseName = DB_NAME_PREFIX."_db_settings_UI";
+ parent::navigateDatabaseSettingsPage( $databaseName );
+
+ // 'Database settings' text available.
+ $this->assertEquals( "Database settings", $this->getText( LINK_DIV."h2" ));
+
+ // 'Database account for web access' section available
+ $this->assertTrue( $this->isElementPresent( LINK_FORM."fieldset" ));
+
+ // 'Database account for web access' label available
+ $this->assertEquals( "Database account for web access", $this->getText( LINK_FORM."fieldset/legend" ));
+
+ // 'Use the same account as for installation' check box available
+ $this->assertEquals( "Use the same account as for installation", $this->getText( LINK_FORM."fieldset/div[1]/label" ));
+
+ // 'Use the same account as for installation' check box is selected by default
+ $this->assertEquals( "on", $this->getValue( "mysql__SameAccount" ));
+
+ // 'Use the same account as for installation' check box deselected
+ $this->click( "mysql__SameAccount" );
+
+ // verify 'Use the same account as for installation' check box is not selected
+ $this->assertEquals( "off", $this->getValue( "mysql__SameAccount" ));
+
+ // 'Database username' label available
+ $this->assertEquals( "Database username:", $this->getText( "//div[@id='dbOtherAccount']/div[1]/div[1]/label" ));
+
+ // 'Database username' text box is default to the 'wikiuser'
+ $this->assertEquals( "wikiuser", $this->getValue( "mysql_wgDBuser" ));
+
+ // 'Database password' label available
+ $this->assertEquals( "Database password:", $this->getText( "//div[@id='dbOtherAccount']/div[2]/div[1]/label" ));
+
+ // 'Create the account if it does not already exist' label available
+ $this->assertEquals( "Create the account if it does not already exist", $this->getText( "//div[@id='dbOtherAccount']/div[4]/label" ));
+
+ // 'Create the account if it does not already exist' check box is not selected by default
+ $this->assertEquals( "off" , $this->getValue( "mysql__CreateDBAccount" ));
+
+ // 'Create the account if it does not already exist' check box selected
+ $this->click( "mysql__CreateDBAccount" );
+
+ // Verify 'Create the account if it does not already exist' check box is selected
+ $this->assertEquals( "on" , $this->getValue( "mysql__CreateDBAccount" ));
+ $this->click( "mysql__SameAccount" );
+ $this->assertEquals( "on", $this->getValue( "mysql__SameAccount" ));
+
+ // 'Storage engine' label available
+ $this->assertEquals( "Storage engine:",
+ $this->getText( LINK_FORM."div[1]/div[1]/label"));
+
+ // 'InnoDB' label available
+ $this->assertEquals( "InnoDB",
+ $this->getText( LINK_FORM."div[1]/div[2]/ul/li[1]/label" ));
+
+ // 'InnoDB' radio button available
+ $this->assertTrue( $this->isElementPresent( "mysql__MysqlEngine_InnoDB" ));
+
+ // 'MyISAM' label available
+ $this->assertEquals( "MyISAM", $this->getText( LINK_FORM."div[1]/div[2]/ul/li[2]/label" ));
+
+ // 'MyISAM' radio button available
+ $this->assertTrue($this->isElementPresent( "mysql__MysqlEngine_MyISAM" ));
+
+ // 'Database character set' label available
+ $this->assertEquals( "Database character set:",
+ $this->getText( LINK_FORM."div[3]/div[1]/label" ));
+
+ // 'Binary' radio button available
+ $this->assertTrue( $this->isElementPresent( "mysql__MysqlCharset_binary" ));
+
+ // 'Binary' radio button available
+ $this->assertEquals( "Binary", $this->getText( LINK_FORM."div[3]/div[2]/ul/li[1]/label" ));
+
+ // 'UTF-8' radio button available
+ $this->assertTrue( $this->isElementPresent( "mysql__MysqlCharset_utf8" ));
+
+ // 'UTF-8' label available
+ $this->assertEquals( "UTF-8", $this->getText( LINK_FORM."div[3]/div[2]/ul/li[2]/label" ));
+
+ // 'Binary' radio button is selected
+ $this->assertEquals( "on", $this->getValue( "mysql__MysqlCharset_binary" ));
+ }
+
+
+
+ public function testNamePageUI() {
+
+ $databaseName = DB_NAME_PREFIX."_name_UI";
+ parent::navigateNamePage($databaseName);
+
+ // 'Name of wiki' text box available
+ $this->assertEquals( "Name of wiki:",
+ $this->getText( LINK_FORM."div[1]/div[1]/label" ));
+
+ $this->assertTrue( $this->isElementPresent( "config_wgSitename" ));
+
+ // 'Project namespace' label available
+ $this->assertEquals( "Project namespace:",
+ $this->getText( LINK_FORM."div[2]/div[1]/label" ));
+
+ // 'Same as the wiki name' radio button available
+ $this->assertTrue( $this->isElementPresent( "config__NamespaceType_site-name" ));
+
+ // 'Project' radio button available
+ $this->assertTrue( $this->isElementPresent( "config__NamespaceType_generic" ));
+
+ // 'Project' radio button available
+ $this->assertTrue( $this->isElementPresent( "config__NamespaceType_other" ));
+
+ // 'Same as the wiki name' label available
+ $this->assertEquals( "Same as the wiki name:",
+ $this->getText( LINK_FORM."div[2]/div[2]/ul/li[1]/label" ));
+
+ // 'Project' label available
+ $this->assertEquals("Project",
+ $this->getText( LINK_FORM."div[2]/div[2]/ul/li[2]/label" ));
+
+ // 'Project' label available
+ $this->assertEquals( "Other (specify)",
+ $this->getText( LINK_FORM."div[2]/div[2]/ul/li[3]/label" ));
+
+ // 'Same as the wiki name' radio button selected by default
+ $this->assertEquals( "on", $this->getValue( "config__NamespaceType_site-name" ));
+
+ // 'Administrator account' section available
+ $this->assertTrue( $this->isElementPresent( LINK_FORM."fieldset" ));
+
+ // 'Administrator account' label available
+ $this->assertEquals( "Administrator account",
+ $this->getText( LINK_FORM."fieldset/legend" ));
+
+ // 'Your Name' label available
+ $this->assertEquals( "Your name:",
+ $this->getText( LINK_FORM."fieldset/div[1]/div[1]/label" ));
+
+ // 'Your Name' text box available
+ $this->assertTrue( $this->isElementPresent( "config__AdminName" ));
+
+ // 'Password' label available
+ $this->assertEquals( "Password:",
+ $this->getText( LINK_FORM."fieldset/div[2]/div[1]/label" ));
+
+ // 'Password' text box available
+ $this->assertTrue( $this->isElementPresent( "config__AdminPassword" ));
+
+ // 'Password again' label available
+ $this->assertEquals( "Password again:",
+ $this->getText( LINK_FORM."fieldset/div[3]/div[1]/label" ));
+
+ // 'Password again' text box available
+ $this->assertTrue( $this->isElementPresent( "config__AdminPassword2" ));
+
+ // 'Email address' label avaialble
+ $this->assertEquals( "E-mail address:",
+ $this->getText( LINK_FORM."fieldset/div[4]/div[1]/label" ));
+
+ // 'Email address' text box available
+ $this->assertTrue( $this->isElementPresent( "config__AdminEmail" ));
+
+ // Message displayed
+ $this->assertEquals( "You are almost done! You can now skip the remaining configuration and install the wiki right now.",
+ $this->getText( LINK_FORM."/div[4]/div[2]/p" ));
+
+ // 'Ask me more questions.' radio button available
+ $this->assertTrue( $this->isElementPresent( "config__SkipOptional_continue" ));
+
+ // 'Ask me more questions.' label available
+ $this->assertEquals( "Ask me more questions.",
+ $this->getText( LINK_FORM."div[5]/div[2]/ul/li[1]/label" ));
+
+ // 'I'm bored already, just install the wiki' radio button is avaiable
+ $this->assertTrue( $this->isElementPresent( "config__SkipOptional_skip" ));
+
+ // 'I'm bored already, just install the wiki' label available
+ $this->assertEquals( "I'm bored already, just install the wiki.",
+ $this->getText( LINK_FORM."div[5]/div[2]/ul/li[2]/label" ));
+
+ // 'Ask me more questions.' radio button is default selected
+ $this->assertEquals( "on", $this->getValue( "config__SkipOptional_continue" ));
+ }
+
+
+
+ public function testOptionPageUI() {
+
+ $databaseName = DB_NAME_PREFIX."_options_UI";
+ parent::navigateOptionsPage($databaseName);
+
+ // 'Options' label available
+ $this->assertEquals( "Options", $this->getText( LINK_DIV."h2"));
+
+ // 'Return e-mail address' label available
+ $this->assertEquals( "Return e-mail address:", $this->getText( "//div[@id='emailwrapper']/div[1]/div[1]/label" ));
+
+ // 'Return e-mail address' text box available
+ $this->assertTrue( $this->isElementPresent( "config_wgPasswordSender" ));
+
+ // Text 'apache@localhost' is default value of the 'Return e-mail address' text box
+ $this->assertEquals( "apache@localhost", $this->getValue( "config_wgPasswordSender" ));
+
+ // 'Logo URL' label available
+ $this->assertEquals( "Logo URL:", $this->getText( LINK_FORM."fieldset[2]/div[3]/div[1]/label" ));
+
+ // 'Logo URL' text box available
+ $this->assertTrue( $this->isElementPresent( "config_wgLogo" ));
+
+ // Correct path available in the 'Logo URL' text box
+ $this->assertEquals( "/wiki/skins/common/images/wiki.png", $this->getValue( "config_wgLogo" ));
+
+ // 'Enable file uploads' radio button available
+ $this->assertTrue( $this->isElementPresent( "config_wgEnableUploads" ));
+
+ // 'Enable file uploads' label available
+ $this->assertEquals( "Enable file uploads",
+ $this->getText( LINK_FORM."fieldset[2]/div[1]/label" ));
+
+ // 'Enable file uploads' check box is not selected
+ $this->assertEquals( "off", $this->getValue( "config_wgEnableUploads" ));
+
+ $this->click( "config_wgEnableUploads" );
+
+ // 'Directory for deleted files' label available
+ $this->assertEquals( "Directory for deleted files:",
+ $this->getText( "//div[@id='uploadwrapper']/div/div[1]/label" ));
+
+ // 'Directory for deleted files' text box available
+ $this->assertTrue( $this->isElementPresent( "config_wgDeletedDirectory" ));
+
+ // Correct path available in the 'Directory for deleted files' text box
+ $this->assertEquals( "C:\\wamp\\www\\".DIRECTORY_NAME."/images/deleted",
+ $this->getValue( "config_wgDeletedDirectory" ));
+ }
+
+
+
+ public function testInstallPageUI() {
+
+ $databaseName = DB_NAME_PREFIX."_install_UI";
+ parent::navigateInstallPage( $databaseName );
+
+ // Verify installation done messages display
+ $this->assertEquals( "Setting up database... done",
+ $this->getText( LINK_FORM."ul/li[1]" ));
+ $this->assertEquals( "Creating tables... done",
+ $this->getText( LINK_FORM."ul/li[2]" ));
+ $this->assertEquals( "Creating database user... done",
+ $this->getText( LINK_FORM."ul/li[3]" ));
+ $this->assertEquals( "Populating default interwiki table... done",
+ $this->getText( LINK_FORM."ul/li[4]" ));
+ $this->assertEquals( "Generating secret key... done",
+ $this->getText( LINK_FORM."ul/li[5]" ));
+ $this->assertEquals( "Generating default upgrade key... done",
+ $this->getText( LINK_FORM."ul/li[6]" ));
+ $this->assertEquals( "Creating administrator user account... done",
+ $this->getText( LINK_FORM."ul/li[7]" ));
+ $this->assertEquals( "Creating main page with default content... done",
+ $this->getText( LINK_FORM."ul/li[8]" ));
+ }
+
+
+
+ public function testCompletePageUI() {
+
+ $databaseName = DB_NAME_PREFIX."_complete_UI";
+ parent::navigateCompletePage( $databaseName );
+
+ // 'Congratulations!' text display
+ $this->assertEquals("Congratulations!",
+ $this->getText( LINK_FORM."div[1]/div[2]/p[1]/b"));
+ // 'LocalSettings.php' generated message display
+ $this->assertEquals( "The installer has generated a LocalSettings.php file. It contains all your configuration.",
+ $this->getText( LINK_FORM."div[1]/div[2]/p[2]" ));
+
+ // 'Download LocalSettings.php'' link available
+ $this->assertTrue( $this->isElementPresent( "link=Download LocalSettings.php" ));
+
+ // 'enter your wiki' link available
+ $this->assertTrue($this->isElementPresent("link=Folder/index.php enter your wiki"));
+ }
+
+
+
+ public function testRestartInstallation() {
+
+ parent::navigateConnetToDatabasePage();
+ $this->click( "link=Restart installation" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // Restart installation' label should be available.
+ $this->assertEquals( "Restart installation", $this->getText( LINK_DIV."h2" ));
+
+ //'Do you want to clear all saved data that you have entered and restart the installation process?' label available
+ $this->assertEquals( "Do you want to clear all saved data that you have entered and restart the installation process?",
+ $this->getText( "//*[@id='bodyContent']/div/div/div[2]/form/div[1]/div[2]" ));
+ // 'Back' button available
+ $this->assertTrue($this->isElementPresent( "submit-back" ));
+
+ // 'Restart' button available
+ $this->assertTrue($this->isElementPresent( "submit-restart" ));
+ }
+
+
+
+ public function testMediaWikiLogoAvailability() {
+
+ $databaseName = DB_NAME_PREFIX."_mediawiki_logo";
+ parent::navigateInitialpage();
+ parent::mediaWikiLogoPresentInitialScreen();
+ $this->click( "link=set up the wiki" );
+ $this->waitForPageToLoad( PAGE_LOAD_TIME );
+
+ // 'Language' page
+ parent::mediaWikiLogoPresent();
+ parent::clickContinueButton();
+
+ // 'Welcome to MediaWiki' page
+ parent::mediaWikiLogoPresent();
+ parent::clickContinueButton();
+
+ // 'Connet to database' page
+ parent::mediaWikiLogoPresent();
+ $this->type("mysql_wgDBname", $databaseName );
+ parent::clickContinueButton();
+
+ // 'Database setting' page
+ parent::mediaWikiLogoPresent();
+ parent::clickContinueButton();
+
+ // 'Name' page
+ parent::mediaWikiLogoPresent();
+ parent::completeNamePage();
+ parent::clickContinueButton();
+
+ // 'Options' page
+ parent::mediaWikiLogoPresent();
+ parent::clickContinueButton();
+
+ // 'Install' page
+ parent::mediaWikiLogoPresent();
+ }
+
+
+ public function testRightFramework() {
+
+ parent::navigateLanguagePage();
+ // Verfy right framework texts display
+ $this->assertEquals( "Language",
+ $this->getText( LINK_RIGHT_FRAMEWORK."li[1]" ));
+ $this->assertEquals( "Existing wiki",
+ $this->getText( LINK_RIGHT_FRAMEWORK."li[2]" ));
+ $this->assertEquals( "Welcome to MediaWiki!",
+ $this->getText( LINK_RIGHT_FRAMEWORK."li[3]" ));
+ $this->assertEquals( "Connect to database",
+ $this->getText( LINK_RIGHT_FRAMEWORK."li[4]" ));
+ $this->assertEquals( "Upgrade existing installation",
+ $this->getText( LINK_RIGHT_FRAMEWORK."li[5]" ));
+ $this->assertEquals( "Database settings",
+ $this->getText( LINK_RIGHT_FRAMEWORK."li[6]" ));
+ $this->assertEquals( "Name",
+ $this->getText( LINK_RIGHT_FRAMEWORK."li[7]" ));
+ $this->assertEquals( "Options",
+ $this->getText( LINK_RIGHT_FRAMEWORK."li[8]" ));
+ $this->assertEquals( "Install",
+ $this->getText( LINK_RIGHT_FRAMEWORK."li[9]" ));
+ $this->assertEquals( "Complete!",
+ $this->getText( LINK_RIGHT_FRAMEWORK."li[10]/span" ));
+ }
+}
diff --git a/tests/selenium/installer/README.txt b/tests/selenium/installer/README.txt
new file mode 100644
index 00000000..83d1a346
--- /dev/null
+++ b/tests/selenium/installer/README.txt
@@ -0,0 +1,32 @@
+== Details==
+
+Automated Selenium test scripts written for MediaWiki Installer is available at svn.wikimedia.org/svnroot/mediawiki/trunk/phase3/tests/selenium/installer.
+Detailed test cases available at http://www.mediawiki.org/wiki/New_installer/Test_plan.
+
+Version : MediaWiki 1.18alpha
+Date : 27/12/2010
+
+== Running tests ==
+
+Test cases can be run independently or can run all the test cases using MediaWikiInstallerTestSuite.php within PHPUnit/Selenium.
+
+
+== Dependencies ==
+
+MediaWikiInstallationConfig.php
+
+Value of the 'DB_NAME_PREFIX' should be replace with the database name prefix. Several DB instances will get created to cover different installation scenarios starting with the above prefix.
+You need to change the value of the 'DB_NAME_PREFIX' in MediaWikiInstallationConfig everytime you planned to
+run the tests.
+'DIRECTORY_NAME', 'PORT' and the 'HOST_NAME' should be replaced with your local values.
+You may specify the test browser you wish to run the test using 'TEST_BROWSER'. Default browser is Firefox.
+
+Note : MediaWikiInstallerTestSuite.php has no dependency on 'Selenium' test framework.
+
+
+== Known problems ==
+
+If you run the MediaWikiInstallerTestSuite.php twice without changing the name of the database, the second run should be falied.
+(Please read the more information on how to change the database name which is avaialable under 'Dependencies' section)
+
+
diff --git a/tests/selenium/selenium_settings.ini.sample b/tests/selenium/selenium_settings.ini.sample
new file mode 100644
index 00000000..b1d88193
--- /dev/null
+++ b/tests/selenium/selenium_settings.ini.sample
@@ -0,0 +1,32 @@
+[SeleniumSettings]
+
+; Set up the available browsers that Selenium can control.
+browsers[firefox] = "*firefox"
+browsers[iexplorer] = "*iexploreproxy"
+browsers[chrome] = "*chrome"
+
+; The simple configurations above usually work on Linux, but Windows and
+; Mac OS X hosts may need to specify a full path:
+;browsers[firefox] = "*firefox /Applications/Firefox.app/Contents/MacOS/firefox-bin"
+;browsers[firefox] = "*firefox C:\Program Files\Mozilla Firefox\firefox.exe"
+
+host = "localhost"
+port = "4444"
+wikiUrl = "http://localhost/deployment"
+username = "wikiuser"
+userPassword = "wikipass"
+testBrowser = "firefox"
+startserver =
+stopserver =
+jUnitLogFile =
+runAgainstGrid = false
+
+; To let the test runner start and stop the selenium server, it needs the full
+; path to selenium-server.jar from the selenium-remote-control package.
+seleniumserverexecpath = "/opt/local/selenium-remote-control-1.0.3/selenium-server-1.0.3/selenium-server.jar"
+
+[SeleniumTests]
+
+testSuite[SimpleSeleniumTestSuite] = "tests/selenium/suites/SimpleSeleniumTestSuite.php"
+testSuite[WikiEditorTestSuite] = "extensions/WikiEditor/selenium/WikiEditorTestSuite.php"
+
diff --git a/tests/selenium/selenium_settings_grid.ini.sample b/tests/selenium/selenium_settings_grid.ini.sample
new file mode 100644
index 00000000..3bbd534a
--- /dev/null
+++ b/tests/selenium/selenium_settings_grid.ini.sample
@@ -0,0 +1,16 @@
+[SeleniumSettings]
+
+host = "grid.tesla.usability.wikimedia.org"
+port = "4444"
+wikiUrl = "http://208.80.152.253:5001"
+username = "wikiuser"
+userPassword = "wikipass"
+testBrowser = "Safari on OS X Snow Leopard"
+jUnitLogFile =
+runAgainstGrid = true
+startserver = false
+stopserver = false
+
+[SeleniumTests]
+
+testSuite[SimpleSeleniumTestSuite] = "tests/selenium/suites/SimpleSeleniumTestSuite.php"
diff --git a/tests/selenium/suites/AddContentToNewPageTestCase.php b/tests/selenium/suites/AddContentToNewPageTestCase.php
new file mode 100644
index 00000000..72e75e11
--- /dev/null
+++ b/tests/selenium/suites/AddContentToNewPageTestCase.php
@@ -0,0 +1,182 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+
+class AddContentToNewPageTestCase extends SeleniumTestCase {
+
+
+ // Add bold text and verify output
+ public function testAddBoldText() {
+
+ $this->getExistingPage();
+ $this->clickEditLink();
+ $this->loadWikiEditor();
+ $this->clearWikiEditor();
+ $this->click( "//*[@id='mw-editbutton-bold']" );
+ $this->clickShowPreviewBtn();
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify bold text displayed on mediawiki preview
+ $this->assertTrue($this->isElementPresent( "//div[@id='wikiPreview']/p/b" ));
+ $this->assertTrue($this->isTextPresent( "Bold text" ));
+ }
+
+ // Add italic text and verify output
+ public function testAddItalicText() {
+
+ $this->getExistingPage();
+ $this->clickEditLink();
+ $this->loadWikiEditor();
+ $this->clearWikiEditor();
+ $this->click( "//*[@id='mw-editbutton-italic']" );
+ $this->clickShowPreviewBtn();
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify italic text displayed on mediawiki preview
+ $this->assertTrue($this->isElementPresent("//div[@id='wikiPreview']/p/i"));
+ $this->assertTrue($this->isTextPresent( "Italic text" ));
+ }
+
+ // Add internal link for a new page and verify output in the preview
+ public function testAddInternalLinkNewPage() {
+ $this->getExistingPage();
+ $this->clickEditLink();
+ $this->loadWikiEditor();
+ $this->clearWikiEditor();
+ $this->click( "//*[@id='mw-editbutton-link']" );
+ $this->clickShowPreviewBtn();
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify internal link displayed on mediawiki preview
+ $source = $this->getText( "//*[@id='wikiPreview']/p/a" );
+ $correct = strstr( $source, "Link title" );
+ $this->assertEquals( $correct, true );
+
+ $this->click( SeleniumTestConstants::LINK_START."Link title" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify internal link open as a new page - editing mode
+ $source = $this->getText( "firstHeading" );
+ $correct = strstr( $source, "Editing Link title" );
+ $this->assertEquals( $correct, true );
+ }
+
+ // Add external link and verify output in the preview
+ public function testAddExternalLink() {
+ $this->getExistingPage();
+ $this->clickEditLink();
+ $this->loadWikiEditor();
+ $this->clearWikiEditor();
+ $this->click( "//*[@id='mw-editbutton-extlink']" );
+ $this->type( SeleniumTestConstants::TEXT_EDITOR, "[http://www.google.com Google]" );
+ $this->clickShowPreviewBtn();
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify external links displayed on mediawiki preview
+ $source = $this->getText( "//*[@id='wikiPreview']/p/a" );
+ $correct = strstr( $source, "Google" );
+ $this->assertEquals( $correct, true );
+
+ $this->click( SeleniumTestConstants::LINK_START."Google" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify external link opens
+ $source = $this->getTitle();
+ $correct = strstr( $source, "Google" );
+ $this->assertEquals( $correct, true);
+ }
+
+ // Add level 2 headline and verify output in the preview
+ public function testAddLevel2HeadLine() {
+ $blnElementPresent = false;
+ $blnTextPresent = false;
+ $this->getExistingPage();
+ $this->clickEditLink();
+ $this->loadWikiEditor();
+ $this->clearWikiEditor();
+ $this->click( "mw-editbutton-headline" );
+ $this->clickShowPreviewBtn();
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertTrue($this->isElementPresent( "//div[@id='wikiPreview']/h2" ));
+
+ // Verify level 2 headline displayed on mediawiki preview
+ $source = $this->getText( "//*[@id='Headline_text']" );
+ $correct = strstr( $source, "Headline text" );
+ $this->assertEquals( $correct, true );
+ }
+
+ // Add text with ignore wiki format and verify output the preview
+ public function testAddNoWikiFormat() {
+ $this->getExistingPage();
+ $this->clickEditLink();
+ $this->loadWikiEditor();
+ $this->clearWikiEditor();
+ $this->click( "//*[@id='mw-editbutton-nowiki']" );
+ $this->clickShowPreviewBtn();
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify ignore wiki format text displayed on mediawiki preview
+ $source = $this->getText( "//div[@id='wikiPreview']/p" );
+ $correct = strstr( $source, "Insert non-formatted text here" );
+ $this->assertEquals( $correct, true );
+ }
+
+ // Add signature and verify output in the preview
+ public function testAddUserSignature() {
+ $this->getExistingPage();
+ $this->clickEditLink();
+ $this->loadWikiEditor();
+ $this->clearWikiEditor();
+ $this->click( "mw-editbutton-signature" );
+ $this->clickShowPreviewBtn();
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify signature displayed on mediawiki preview
+ $source = $this->getText( "//*[@id='wikiPreview']/p/a" );
+ $username = $this->getText( "//*[@id='pt-userpage']/a" );
+ $correct = strstr( $source, $username );
+ $this->assertEquals( $correct, true );
+ }
+
+ // Add horizontal line and verify output in the preview
+ public function testHorizontalLine() {
+ $this->getExistingPage();
+ $this->clickEditLink();
+ $this->loadWikiEditor();
+ $this->clearWikiEditor();
+ $this->click( "mw-editbutton-hr" );
+
+ $this->clickShowPreviewBtn();
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify horizontal line displayed on mediawiki preview
+ $this->assertTrue( $this->isElementPresent( "//div[@id='wikiPreview']/hr" ));
+ $this->deletePage( "new" );
+ }
+}
diff --git a/tests/selenium/suites/AddNewPageTestCase.php b/tests/selenium/suites/AddNewPageTestCase.php
new file mode 100644
index 00000000..f3302e5e
--- /dev/null
+++ b/tests/selenium/suites/AddNewPageTestCase.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+
+class AddNewPageTestCase extends SeleniumTestCase {
+
+ // Verify adding a new page
+ public function testAddNewPage() {
+ $newPage = "new";
+ $displayName = "New";
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->type( "searchInput", $newPage );
+ $this->click( "searchGoButton" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify 'Search results' text available
+ $source = $this->gettext( "firstHeading" );
+ $correct = strstr( $source, "Search results" );
+ $this->assertEquals( $correct, true);
+
+ // Verify 'Create the page "<page name>" on this wiki' text available
+ $source = $this->gettext( "//div[@id='bodyContent']/div[4]/p/b" );
+ $correct = strstr ( $source, "Create the page \"New\" on this wiki!" );
+ $this->assertEquals( $correct, true );
+
+ $this->click( SeleniumTestConstants::LINK_START.$displayName );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->assertTrue($this->isElementPresent( SeleniumTestConstants::LINK_START."Create" ));
+ $this->type( "wpTextbox1", "add new test page" );
+ $this->click( SeleniumTestConstants::BUTTON_SAVE );
+
+ // Verify new page added
+ $source = $this->gettext( "firstHeading" );
+ $correct = strstr ( $source, $displayName );
+ $this->assertEquals( $correct, true );
+ }
+}
diff --git a/tests/selenium/suites/CreateAccountTestCase.php b/tests/selenium/suites/CreateAccountTestCase.php
new file mode 100644
index 00000000..5708bcff
--- /dev/null
+++ b/tests/selenium/suites/CreateAccountTestCase.php
@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+Class CreateAccountTestCase extends SeleniumTestCase {
+
+ // Change these values before run the test
+ private $userName = "yourname4000";
+ private $password = "yourpass4000";
+
+ // Verify 'Log in/create account' link existance in Main page.
+ public function testMainPageLink() {
+
+ $this->click( "link=Log out" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->open( $this->getUrl().'/index.php?title=Main_Page' );
+ $this->assertTrue($this->isElementPresent( "link=Log in / create account" ));
+ }
+
+ // Verify 'Create an account' link existance in 'Log in / create account' Page.
+ public function testCreateAccountPageLink() {
+
+ $this->click( "link=Log out" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->open( $this->getUrl().'/index.php?title=Main_Page' );
+
+ // click Log in / create account link to open Log in / create account' page
+ $this->click( "link=Log in / create account" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertTrue($this->isElementPresent( "link=Create an account" ));
+ }
+
+ // Verify Create account
+ public function testCreateAccount() {
+
+ $this->click( "link=Log out" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->open( $this->getUrl().'/index.php?title=Main_Page' );
+
+ $this->click( "link=Log in / create account" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->click( "link=Create an account" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify for blank user name
+ $this->type( "wpName2", "" );
+ $this->click( "wpCreateaccount" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( "Login error\n You have not specified a valid user name.",
+ $this->getText( "//div[@id='bodyContent']/div[4]" ));
+
+ // Verify for invalid user name
+ $this->type( "wpName2", "@" );
+ $this->click("wpCreateaccount" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( "Login error\n You have not specified a valid user name.",
+ $this->getText( "//div[@id='bodyContent']/div[4]" ));
+
+ // start of test for blank password
+ $this->type( "wpName2", $this->userName);
+ $this->type( "wpPassword2", "" );
+ $this->click( "wpCreateaccount" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( "Login error\n Passwords must be at least 1 character.",
+ $this->getText("//div[@id='bodyContent']/div[4]" ));
+
+ $this->type( "wpName2", $this->userName );
+ $this->type( "wpPassword2", $this->password );
+ $this->click( "wpCreateaccount" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( "Login error\n The passwords you entered do not match.",
+ $this->getText( "//div[@id='bodyContent']/div[4]" ));
+
+ $this->type( "wpName2", $this->userName );
+ $this->type( "wpPassword2", $this->password );
+ $this->type( "wpRetype", $this->password );
+ $this->click( "wpCreateaccount" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify successful account creation for valid combination of 'Username', 'Password', 'Retype password'
+ $this->assertEquals( "Welcome, ".ucfirst( $this->userName )."!",
+ $this->getText( "Welcome,_".ucfirst( $this->userName )."!" ));
+ }
+}
+
diff --git a/tests/selenium/suites/DeletePageAdminTestCase.php b/tests/selenium/suites/DeletePageAdminTestCase.php
new file mode 100644
index 00000000..9898188f
--- /dev/null
+++ b/tests/selenium/suites/DeletePageAdminTestCase.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+
+class DeletePageAdminTestCase extends SeleniumTestCase {
+
+ // Verify adding a new page
+ public function testDeletePage() {
+
+
+ $newPage = "new";
+ $displayName = "New";
+
+ $this->open( $this->getUrl().'/index.php?title=Main_Page' );
+
+ $this->type( "searchInput", $newPage );
+ $this->click( "searchGoButton" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->click( SeleniumTestConstants::LINK_START.$displayName );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->type( SeleniumTestConstants::TEXT_EDITOR, $newPage." text" );
+ $this->click( SeleniumTestConstants::BUTTON_SAVE );
+
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->click( SeleniumTestConstants::LINK_START."Log out" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->click( SeleniumTestConstants::LINK_START."Log in / create account" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->type( "wpName1", $this->selenium->getUser() );
+ $this->type( "wpPassword1", $this->selenium->getPass() );
+ $this->click( "wpLoginAttempt" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->type( "searchInput", "new" );
+ $this->click( "searchGoButton");
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify 'Delete' link displayed
+ $source = $this->gettext( SeleniumTestConstants::LINK_START."Delete" );
+ $correct = strstr ( $source, "Delete" );
+ $this->assertEquals($correct, true );
+
+ $this->click( SeleniumTestConstants::LINK_START."Delete" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify 'Delete' button available
+ $this->assertTrue($this->isElementPresent( "wpConfirmB" ));
+
+ $this->click( "wpConfirmB" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify 'Action complete' text displayed
+ $source = $this->gettext( "firstHeading" );
+ $correct = strstr ( $source, "Action complete" );
+ $this->assertEquals( $correct, true );
+
+ // Verify '<Page Name> has been deleted. See deletion log for a record of recent deletions.' text displayed
+ $source = $this->gettext( "//div[@id='bodyContent']/p[1]" );
+ $correct = strstr ( $source, "\"New\" has been deleted. See deletion log for a record of recent deletions." );
+ $this->assertEquals( $correct, true );
+ }
+}
diff --git a/tests/selenium/suites/EmailPasswordTestCase.php b/tests/selenium/suites/EmailPasswordTestCase.php
new file mode 100644
index 00000000..88d9cf97
--- /dev/null
+++ b/tests/selenium/suites/EmailPasswordTestCase.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+class EmailPasswordTestCase extends SeleniumTestCase {
+
+ // change user name for each and every test (with in 24 hours)
+ private $userName = "test1";
+
+ public function testEmailPasswordButton() {
+
+ $this->click( SeleniumTestConstants::LINK_START."Log out" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->open( $this->getUrl().'/index.php?title=Main_Page' );
+
+ // click Log in / create account link to open Log in / create account' page
+ $this->click( SeleniumTestConstants::LINK_START."Log in / create account" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertTrue($this->isElementPresent( "wpMailmypassword" ));
+ }
+
+ // Verify Email password functionality
+ public function testEmailPasswordMessages() {
+
+ $this->click( SeleniumTestConstants::LINK_START."Log out" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->open( $this->getUrl().'/index.php?title=Main_Page' );
+
+ // click Log in / create account link to open Log in / create account' page
+ $this->click( SeleniumTestConstants::LINK_START."Log in / create account" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->type( "wpName1", "" );
+ $this->click( "wpMailmypassword" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( "Login error\n You have not specified a valid user name.",
+ $this->getText("//div[@id='bodyContent']/div[4]"));
+
+ $this->type( "wpName1", $this->userName );
+ $this->click( "wpMailmypassword" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Can not run on localhost
+ $this->assertEquals( "A new password has been sent to the e-mail address registered for ".ucfirst($this->userName).". Please log in again after you receive it.",
+ $this->getText("//div[@id='bodyContent']/div[4]" ));
+
+ $this->type( "wpName1", $this->userName );
+ $this->click( "wpMailmypassword" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( "Login error\n A password reminder has already been sent, within the last 24 hours. To prevent abuse, only one password reminder will be sent per 24 hours.",
+ $this->getText( "//div[@id='bodyContent']/div[4]" ));
+ }
+}
+
diff --git a/tests/selenium/suites/MediaWikiEditorConfig.php b/tests/selenium/suites/MediaWikiEditorConfig.php
new file mode 100644
index 00000000..072c3cb2
--- /dev/null
+++ b/tests/selenium/suites/MediaWikiEditorConfig.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+class MediaWikiEditorConfig {
+
+ public static function getSettings(&$includeFiles, &$globalConfigs) {
+ $includes = array(
+ //files that needed to be included would go here
+ //commenting out because this does not exist
+ //'tests/selenium/suites/MediaWikiCommonFunction.php'
+ );
+ $configs = array(
+ 'wgPageLoadTime' => "600000"
+ );
+ $includeFiles = array_merge( $includeFiles, $includes );
+ $globalConfigs = array_merge( $globalConfigs, $configs);
+ return true;
+ }
+}
+
+
+
diff --git a/tests/selenium/suites/MediaWikiEditorTestSuite.php b/tests/selenium/suites/MediaWikiEditorTestSuite.php
new file mode 100644
index 00000000..c0aee9f5
--- /dev/null
+++ b/tests/selenium/suites/MediaWikiEditorTestSuite.php
@@ -0,0 +1,18 @@
+<?php
+
+class MediaWikiEditorTestSuite extends SeleniumTestSuite {
+ public function setUp() {
+ $this->setLoginBeforeTests( true );
+ parent::setUp();
+ }
+ public function addTests() {
+ $testFiles = array(
+ 'tests/selenium/suites/AddNewPageTestCase.php',
+ 'tests/selenium/suites/AddContentToNewPageTestCase.php',
+ 'tests/selenium/suites/PreviewPageTestCase.php',
+ 'tests/selenium/suites/SavePageTestCase.php',
+ );
+ parent::addTestFiles( $testFiles );
+ }
+}
+
diff --git a/tests/selenium/suites/MediaWikiExtraTestSuite.php b/tests/selenium/suites/MediaWikiExtraTestSuite.php
new file mode 100644
index 00000000..5cd0a349
--- /dev/null
+++ b/tests/selenium/suites/MediaWikiExtraTestSuite.php
@@ -0,0 +1,20 @@
+<?php
+
+class MediaWikiExtraTestSuite extends SeleniumTestSuite {
+ public function setUp() {
+ $this->setLoginBeforeTests( true );
+ parent::setUp();
+ }
+ public function addTests() {
+ $testFiles = array(
+ 'tests/selenium/suites/MyContributionsTestCase.php',
+ 'tests/selenium/suites/MyWatchListTestCase.php',
+ 'tests/selenium/suites/UserPreferencesTestCase.php',
+ 'tests/selenium/suites/MovePageTestCase.php',
+ 'tests/selenium/suites/PageSearchTestCase.php',
+ 'tests/selenium/suites/EmailPasswordTestCase.php',
+ 'tests/selenium/suites/CreateAccountTestCase.php'
+ );
+ parent::addTestFiles( $testFiles );
+ }
+}
diff --git a/tests/selenium/suites/MediawikiCoreSmokeTestCase.php b/tests/selenium/suites/MediawikiCoreSmokeTestCase.php
new file mode 100644
index 00000000..7b9525af
--- /dev/null
+++ b/tests/selenium/suites/MediawikiCoreSmokeTestCase.php
@@ -0,0 +1,69 @@
+<?php
+/*
+ * Stub of tests be need as part of the hack-a-ton
+ */
+class MediawikiCoreSmokeTestCase extends SeleniumTestCase {
+ public function testUserLogin() {
+
+ }
+
+ public function testChangeUserPreference() {
+
+ }
+
+ /*
+ * TODO: generalize this test to be reusable for different skins
+ */
+ public function testCreateNewPageVector() {
+
+ }
+
+ /*
+ * TODO: generalize this test to be reusable for different skins
+ */
+ public function testEditExistingPageVector() {
+
+ }
+
+ /*
+ * TODO: generalize this test to be reusable for different skins
+ */
+ public function testCreateNewPageMonobook() {
+
+ }
+
+ /*
+ * TODO: generalize this test to be reusable for different skins
+ */
+ public function testEditExistingPageMonobook() {
+
+ }
+
+ public function testImageUpload() {
+ $this->login();
+ $this->open( $this->getUrl() .
+ '/index.php?title=Special:Upload' );
+ $this->type( 'wpUploadFile', dirname( __FILE__ ) .
+ "\\..\\data\\Wikipedia-logo-v2-de.png" );
+ $this->check( 'wpIgnoreWarning' );
+ $this->click( 'wpUpload' );
+ $this->waitForPageToLoad( 30000 );
+
+ $this->assertSeleniumHTMLContains(
+ '//h1[@class="firstHeading"]', "Wikipedia-logo-v2-de.png" );
+
+ /*
+ $this->open( $this->getUrl() . '/index.php?title=Image:'
+ . ucfirst( $this->filename ) . '&action=delete' );
+ $this->type( 'wpReason', 'Remove test file' );
+ $this->click( 'mw-filedelete-submit' );
+ $this->waitForPageToLoad( 10000 );
+
+ // Todo: This message is localized
+ $this->assertSeleniumHTMLContains( '//div[@id="bodyContent"]/p',
+ ucfirst( $this->filename ) . '.*has been deleted.' );
+ */
+ }
+
+
+}
diff --git a/tests/selenium/suites/MediawikiCoreSmokeTestSuite.php b/tests/selenium/suites/MediawikiCoreSmokeTestSuite.php
new file mode 100644
index 00000000..5d5ef518
--- /dev/null
+++ b/tests/selenium/suites/MediawikiCoreSmokeTestSuite.php
@@ -0,0 +1,19 @@
+<?php
+/*
+ * Stubs for now. We're going to start populating this test.
+ */
+class MediawikiCoreSmokeTestSuite extends SeleniumTestSuite
+{
+ public function setUp() {
+ $this->setLoginBeforeTests( false );
+ parent::setUp();
+ }
+ public function addTests() {
+ $testFiles = array(
+ 'tests/selenium/suites/MediawikiCoreSmokeTestCase.php'
+ );
+ parent::addTestFiles( $testFiles );
+ }
+
+
+}
diff --git a/tests/selenium/suites/MovePageTestCase.php b/tests/selenium/suites/MovePageTestCase.php
new file mode 100644
index 00000000..5263e7bb
--- /dev/null
+++ b/tests/selenium/suites/MovePageTestCase.php
@@ -0,0 +1,117 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+class MovePageTestCase extends SeleniumTestCase {
+
+ // Verify move(rename) wiki page
+ public function testMovePage() {
+
+ $newPage = "mypage99";
+ $displayName = "Mypage99";
+
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->type( "searchInput", $newPage );
+ $this->click( "searchGoButton" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->click( "link=".$displayName );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->type( SeleniumTestConstants::TEXT_EDITOR, $newPage." text" );
+ $this->click( SeleniumTestConstants::BUTTON_SAVE );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify link 'Move' available
+ $this->assertTrue($this->isElementPresent( "link=Move" ));
+
+ $this->click( "link=Move" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify correct page name displayed under 'Move Page' field
+ $this->assertEquals($displayName,
+ $this->getText("//table[@id='mw-movepage-table']/tbody/tr[1]/td[2]/strong/a"));
+ $movePageName = $this->getText( "//table[@id='mw-movepage-table']/tbody/tr[1]/td[2]/strong/a" );
+
+ // Verify 'To new title' field has current page name as the default name
+ $newTitle = $this->getValue( "wpNewTitle" );
+ $correct = strstr( $movePageName , $newTitle );
+ $this->assertEquals( $correct, true );
+
+ $this->type( "wpNewTitle", $displayName );
+ $this->click( "wpMove" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify warning message for the same source and destination titles
+ $this->assertEquals( "Source and destination titles are the same; cannot move a page over itself.",
+ $this->getText("//div[@id='bodyContent']/p[4]/strong" ));
+
+ // Verify warning message for the blank title
+ $this->type( "wpNewTitle", "" );
+ $this->click( "wpMove" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify warning message for the blank title
+ $this->assertEquals( "The requested page title was invalid, empty, or an incorrectly linked inter-language or inter-wiki title. It may contain one or more characters which cannot be used in titles.",
+ $this->getText( "//div[@id='bodyContent']/p[4]/strong" ));
+
+ // Verify warning messages for the invalid titles
+ $this->type( "wpNewTitle", "# < > [ ] | { }" );
+ $this->click( "wpMove" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( "The requested page title was invalid, empty, or an incorrectly linked inter-language or inter-wiki title. It may contain one or more characters which cannot be used in titles.",
+ $this->getText( "//div[@id='bodyContent']/p[4]/strong" ));
+
+ $this->type( "wpNewTitle", $displayName."move" );
+ $this->click( "wpMove" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify move success message displayed correctly
+ $this->assertEquals( "\"".$displayName."\" has been moved to \"".$displayName."move"."\"",
+ $this->getText( "//div[@id='bodyContent']/p[1]/b" ));
+
+ $this->type( "searchInput", $newPage."move" );
+ $this->click( "searchGoButton" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify search using new page name
+ $this->assertEquals( $displayName."move", $this->getText( "firstHeading" ));
+
+ $this->type( "searchInput", $newPage );
+ $this->click( "searchGoButton" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify search using old page name
+ $redirectPageName = $this->getText( "//*[@id='contentSub']" );
+ $this->assertEquals( "(Redirected from ".$displayName.")" , $redirectPageName );
+
+ // newpage delete
+ $this->deletePage( $newPage."move" );
+ $this->deletePage( $newPage );
+ }
+}
+
diff --git a/tests/selenium/suites/MyContributionsTestCase.php b/tests/selenium/suites/MyContributionsTestCase.php
new file mode 100644
index 00000000..01d87e4b
--- /dev/null
+++ b/tests/selenium/suites/MyContributionsTestCase.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+require_once dirname( dirname( __FILE__ ) ) . '/SeleniumTestConstants.php';
+
+class MyContributionsTestCase extends SeleniumTestCase {
+
+ // Verify user contributions
+ public function testRecentChangesAvailability() {
+
+ $newPage = $this->createNewTestPage( "MyContributionsTest" );
+
+ // Verify My contributions Link available
+ $this->assertTrue($this->isElementPresent( "link=Contributions" ));
+
+
+ $this->click( "link=Contributions" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify recent page adding available on My Contributions list
+ $this->assertEquals( $newPage, $this->getText( "link=".$newPage ));
+
+ $this->type( SeleniumTestConstants::INPUT_SEARCH_BOX, $newPage );
+ $this->click( SeleniumTestConstants::BUTTON_SEARCH );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->click( SeleniumTestConstants::LINK_EDIT );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->type( SeleniumTestConstants::TEXT_EDITOR, $newPage . " text changed" );
+ $this->click( SeleniumTestConstants::BUTTON_SAVE );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->click( "link=Contributions" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify recent page changes available on My Contributions
+ $this->assertTrue( $this->isTextPresent( $newPage ) );
+ }
+}
+
diff --git a/tests/selenium/suites/MyWatchListTestCase.php b/tests/selenium/suites/MyWatchListTestCase.php
new file mode 100644
index 00000000..d1ee3e78
--- /dev/null
+++ b/tests/selenium/suites/MyWatchListTestCase.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+require_once dirname( dirname( __FILE__ ) ) . '/SeleniumTestConstants.php';
+
+class MyWatchListTestCase extends SeleniumTestCase {
+
+ // Verify user watchlist
+ public function testMyWatchlist() {
+
+ $pageName = $this->createNewTestPage( "MyWatchListTest", true );
+ // Verify link 'My Watchlist' available
+ $this->assertTrue( $this->isElementPresent( SeleniumTestConstants::LINK_START."Watchlist" ) );
+
+ $this->click( SeleniumTestConstants::LINK_START."Watchlist" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify newly added page to the watchlist is available
+ $this->assertEquals( $pageName, $this->getText( SeleniumTestConstants::LINK_START.$pageName ));
+
+ $this->click( SeleniumTestConstants::LINK_START.$pageName );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->click( SeleniumTestConstants::LINK_EDIT );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->click( "wpWatchthis" );
+ $this->click( SeleniumTestConstants::BUTTON_SAVE );
+ $this->assertFalse( $this->isElementPresent( SeleniumTestConstants::LINK_START.$pageName ) );
+ //todo watch using the dropdown menu
+ }
+}
+
diff --git a/tests/selenium/suites/PageDeleteTestSuite.php b/tests/selenium/suites/PageDeleteTestSuite.php
new file mode 100644
index 00000000..256e3542
--- /dev/null
+++ b/tests/selenium/suites/PageDeleteTestSuite.php
@@ -0,0 +1,16 @@
+<?php
+
+class PageDeleteTestSuite extends SeleniumTestSuite {
+ public function setUp() {
+ $this->setLoginBeforeTests( true );
+ parent::setUp();
+ }
+ public function addTests() {
+ $testFiles = array(
+ 'tests/selenium/suites/DeletePageAdminTestCase.php'
+ );
+ parent::addTestFiles( $testFiles );
+ }
+
+
+}
diff --git a/tests/selenium/suites/PageSearchTestCase.php b/tests/selenium/suites/PageSearchTestCase.php
new file mode 100644
index 00000000..fe71eada
--- /dev/null
+++ b/tests/selenium/suites/PageSearchTestCase.php
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+class PageSearchTestCase extends SeleniumTestCase {
+
+ // Verify the functionality of the 'Go' button
+ public function testPageSearchBtnGo() {
+
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->type( SeleniumTestConstants::INPUT_SEARCH_BOX, "calcey qa" );
+ $this->click( "searchGoButton" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify no page matched with the entered search text
+ $source = $this->gettext( "//div[@id='bodyContent']/div[4]/p/b" );
+ $correct = strstr ( $source, "Create the page \"Calcey qa\" on this wiki!" );
+ $this->assertEquals( $correct, true );
+
+ $this->click( "link=Calcey qa" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->type( SeleniumTestConstants::TEXT_EDITOR , "Calcey QA team" );
+ $this->click( "wpSave" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ }
+
+ // Verify the functionality of the 'Search' button
+ public function testPageSearchBtnSearch() {
+
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->type( SeleniumTestConstants::INPUT_SEARCH_BOX, "Calcey web" );
+ $this->click( SeleniumTestConstants::BUTTON_SEARCH );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify no page is available as the search text
+ $source = $this->gettext( "//div[@id='bodyContent']/div[4]/p[2]/b" );
+ $correct = strstr ( $source, "Create the page \"Calcey web\" on this wiki!" );
+ $this->assertEquals( $correct, true );
+
+ $this->click( "link=Calcey web" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->type( SeleniumTestConstants::TEXT_EDITOR, "Calcey web team" );
+ $this->click( SeleniumTestConstants::BUTTON_SAVE );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify saved page is opened when the exact page name is given
+ $this->type( SeleniumTestConstants::INPUT_SEARCH_BOX, "Calcey web" );
+ $this->click( SeleniumTestConstants::BUTTON_SEARCH );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify exact page matched with the entered search text using 'Search' button
+ $source = $this->getText( "//*[@id='bodyContent']/div[4]/p/b" );
+ $correct = strstr( $source, "There is a page named \"Calcey web\" on this wiki." );
+ $this->assertEquals( $correct, true );
+
+ // Verify resutls available when partial page name is entered as the search text
+ $this->type( SeleniumTestConstants::INPUT_SEARCH_BOX, "Calcey" );
+ $this->click( SeleniumTestConstants::BUTTON_SEARCH );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify text avaialble in the search result under the page titles
+ if($this->isElementPresent( "Page_title_matches" )) {
+ $textPageTitle = $this->getText( "//*[@id='bodyContent']/div[4]/ul[1]/li[1]/div[1]/a" );
+ $this->assertContains( 'Calcey', $textPageTitle );
+ }
+
+ // Verify text avaialble in the search result under the page text
+ if($this->isElementPresent( "Page_text_matches" )) {
+ $textPageText = $this->getText( "//*[@id='bodyContent']/div[4]/ul[2]/li[2]/div[2]/span" );
+ $this->assertContains( 'Calcey', $textPageText );
+ }
+ $this->deletePage("Calcey QA");
+ $this->deletePage("Calcey web");
+ }
+}
diff --git a/tests/selenium/suites/PreviewPageTestCase.php b/tests/selenium/suites/PreviewPageTestCase.php
new file mode 100644
index 00000000..32206b98
--- /dev/null
+++ b/tests/selenium/suites/PreviewPageTestCase.php
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+class PreviewPageTestCase extends SeleniumTestCase {
+
+ // Verify adding a new page
+ public function testPreviewPage() {
+ $wikiText = "Adding this page to test the \n Preview button functionality";
+ $newPage = "Test Preview Page";
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->getNewPage( $newPage );
+ $this->type( SeleniumTestConstants::TEXT_EDITOR, $wikiText."" );
+ $this->assertTrue($this->isElementPresent( "//*[@id='wpPreview']" ));
+
+ $this->click( "wpPreview" );
+
+ // Verify saved page available
+ $source = $this->gettext( "firstHeading" );
+ $correct = strstr( $source, "Test Preview Page" );
+ $this->assertEquals( $correct, true);
+
+ // Verify page content previewed succesfully
+ $contentOfPreviewPage = $this->getText( "//*[@id='content']" );
+ $this->assertContains( $wikiText, $contentOfPreviewPage );
+ }
+}
diff --git a/tests/selenium/suites/SavePageTestCase.php b/tests/selenium/suites/SavePageTestCase.php
new file mode 100644
index 00000000..310ff20a
--- /dev/null
+++ b/tests/selenium/suites/SavePageTestCase.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+class SavePageTestCase extends SeleniumTestCase {
+
+ // Verify adding a new page
+ public function testSavePage() {
+ $wikiText = "Adding this page to test the Save button functionality";
+ $newPage = "Test Save Page";
+
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->getNewPage($newPage);
+ $this->type( SeleniumTestConstants::TEXT_EDITOR, $wikiText );
+
+ // verify 'Save' button available
+ $this->assertTrue($this->isElementPresent( SeleniumTestConstants::BUTTON_SAVE ));
+ $this->click( SeleniumTestConstants::BUTTON_SAVE );
+
+ // Verify saved page available
+ $source = $this->gettext( "firstHeading" );
+ $correct = strstr( $source, "Test Save Page" );
+
+ // Verify Saved page name displayed correctly
+ $this->assertEquals( $correct, true );
+
+ // Verify page content saved succesfully
+ $contentOfSavedPage = $this->getText( "//*[@id='content']" );
+ $this->assertContains( $wikiText, $contentOfSavedPage );
+ $this->deletePage( $newPage );
+ }
+}
diff --git a/tests/selenium/suites/SimpleSeleniumConfig.php b/tests/selenium/suites/SimpleSeleniumConfig.php
new file mode 100644
index 00000000..54def35a
--- /dev/null
+++ b/tests/selenium/suites/SimpleSeleniumConfig.php
@@ -0,0 +1,30 @@
+<?php
+class SimpleSeleniumConfig {
+
+ public static function getSettings(&$includeFiles, &$globalConfigs, &$resourceFiles) {
+ global $IP;
+ $includes = array(
+ //files that needed to be included would go here
+ );
+ $configs = array(
+ 'wgDBprefix' => 'mw_',
+ 'wgDBTableOptions' => 'ENGINE=InnoDB, DEFAULT CHARSET=binary',
+ 'wgDBmysql5' => 'false',
+ 'wgMainCacheType' => 'CACHE_NONE',
+ 'wgParserCacheType' => 'CACHE_NONE',
+ 'wgMemCachedServers'=> array(),
+ 'wgLanguageCode' => 'en',
+ 'wgSitename' => 'test_wiki',
+ 'wgDefaultSkin' => 'chick'
+ );
+ $resources = array(
+ 'db' => "$IP/tests/selenium/data/SimpleSeleniumTestDB.sql",
+ 'images' => "$IP/tests/selenium/data/SimpleSeleniumTestImages.zip"
+ );
+
+ $includeFiles = array_merge( $includeFiles, $includes );
+ $globalConfigs = array_merge( $globalConfigs, $configs);
+ $resourceFiles = array_merge( $resourceFiles, $resources );
+ return true;
+ }
+} \ No newline at end of file
diff --git a/tests/selenium/suites/SimpleSeleniumTestCase.php b/tests/selenium/suites/SimpleSeleniumTestCase.php
new file mode 100644
index 00000000..99a75c12
--- /dev/null
+++ b/tests/selenium/suites/SimpleSeleniumTestCase.php
@@ -0,0 +1,39 @@
+<?php
+/*
+ * This test case is part of the SimpleSeleniumTestSuite.
+ * Configuration for these tests are documented as part of SimpleSeleniumTestSuite.php
+ */
+class SimpleSeleniumTestCase extends SeleniumTestCase {
+ public function testBasic() {
+ $this->open( $this->getUrl() .
+ '/index.php?title=Selenium&action=edit' );
+ $this->type( "wpTextbox1", "This is a basic test" );
+ $this->click( "wpPreview" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // check result
+ $source = $this->getText( "//div[@id='wikiPreview']/p" );
+ $correct = strstr( $source, "This is a basic test" );
+ $this->assertEquals( $correct, true );
+ }
+
+ /*
+ * All this test really does is verify that a global var was set.
+ * It depends on $wgDefaultSkin = 'chick'; being set
+ */
+ public function testGlobalVariableForDefaultSkin() {
+ $this->open( $this->getUrl() . '/index.php' );
+ $bodyClass = $this->getAttribute( "//body/@class" );
+ $this-> assertContains('skin-chick', $bodyClass, 'Chick skin not set');
+ }
+
+ /*
+ * Just verify that the test db was loaded correctly
+ */
+ public function testDatabaseResourceLoadedCorrectly() {
+ $this->open( $this->getUrl() . '/index.php/TestResources?action=purge' );
+ $testString = $this->gettext( "//body//*[@id='firstHeading']" );
+ $this-> assertEquals('TestResources', $testString, 'Article that should be present in the test db was not found.');
+ }
+
+}
diff --git a/tests/selenium/suites/SimpleSeleniumTestSuite.php b/tests/selenium/suites/SimpleSeleniumTestSuite.php
new file mode 100644
index 00000000..3f5e3645
--- /dev/null
+++ b/tests/selenium/suites/SimpleSeleniumTestSuite.php
@@ -0,0 +1,26 @@
+<?php
+/*
+ * Sample test suite.
+ * Two ways to configure MW for these tests
+ * 1) If you are running multiple test suites, add the following in LocalSettings.php
+ * require_once("tests/selenium/SimpleSeleniumConfig.php");
+ * $wgSeleniumTestConfigs['SimpleSeleniumTestSuite'] = 'SimpleSeleniumConfig::getSettings';
+ * OR
+ * 2) Add the following to your Localsettings.php
+ * $wgDefaultSkin = 'chick';
+ */
+class SimpleSeleniumTestSuite extends SeleniumTestSuite
+{
+ public function setUp() {
+ $this->setLoginBeforeTests( false );
+ parent::setUp();
+ }
+ public function addTests() {
+ $testFiles = array(
+ 'selenium/suites/SimpleSeleniumTestCase.php'
+ );
+ parent::addTestFiles( $testFiles );
+ }
+
+
+}
diff --git a/tests/selenium/suites/UserPreferencesTestCase.php b/tests/selenium/suites/UserPreferencesTestCase.php
new file mode 100644
index 00000000..3ceb3a99
--- /dev/null
+++ b/tests/selenium/suites/UserPreferencesTestCase.php
@@ -0,0 +1,179 @@
+<?php
+
+/**
+ * Selenium server manager
+ *
+ * @file
+ * @ingroup Testing
+ * Copyright (C) 2010 Nadeesha Weerasinghe <nadeesha@calcey.com>
+ * http://www.calcey.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @addtogroup Testing
+ *
+ */
+
+class UserPreferencesTestCase extends SeleniumTestCase {
+
+ // Verify user information
+ public function testUserInfoDisplay() {
+
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->click( SeleniumTestConstants::LINK_START."My preferences" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify correct username displayed in User Preferences
+ $this->assertEquals( $this->getText( "//li[@id='pt-userpage']/a" ),
+ $this->getText( "//table[@id='mw-htmlform-info']/tbody/tr[1]/td[2]" ));
+
+ // Verify existing Signature Displayed correctly
+ $this->assertEquals( $this->selenium->getUser(),
+ $this->getTable( "mw-htmlform-signature.0.1" ) );
+ }
+
+ // Verify change password
+ public function testChangePassword() {
+
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->click( SeleniumTestConstants::LINK_START."My preferences" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->click( SeleniumTestConstants::LINK_START."Change password" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->type( "wpPassword", "12345" );
+ $this->type( "wpNewPassword", "54321" );
+ $this->type( "wpRetype", "54321" );
+ $this->click( "//input[@value='Change password']" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->assertEquals( "Preferences", $this->getText( "firstHeading" ));
+
+ $this->click( SeleniumTestConstants::LINK_START."Change password" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->type( "wpPassword", "54321" );
+ $this->type( "wpNewPassword", "12345" );
+ $this->type( "wpRetype", "12345" );
+ $this->click( "//input[@value='Change password']" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->assertEquals( "Preferences", $this->getText( "firstHeading" ));
+
+ $this->click( SeleniumTestConstants::LINK_START."Change password" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->type( "wpPassword", "54321" );
+ $this->type( "wpNewPassword", "12345" );
+ $this->type( "wpRetype", "12345" );
+ $this->click( "//input[@value='Change password']" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ }
+
+ // Verify successful preferences save
+ public function testSuccessfullSave() {
+
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->click( SeleniumTestConstants::LINK_START."My preferences" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->type( "mw-input-realname", "Test User" );
+ $this->click( "prefcontrol" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify "Your preferences have been saved." message
+ $this->assertEquals( "Your preferences have been saved.",
+ $this->getText( "//div[@id='bodyContent']/div[4]/strong/p" ));
+ $this->type( "mw-input-realname", "" );
+ $this->click( "prefcontrol" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ }
+
+ // Verify change signature
+ public function testChangeSignature() {
+
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->click( SeleniumTestConstants::LINK_START."My preferences" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->type( "mw-input-nickname", "TestSignature" );
+ $this->click( "prefcontrol" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify change user signature
+ $this->assertEquals( "TestSignature", $this->getText( SeleniumTestConstants::LINK_START."TestSignature" ));
+ $this->type( "mw-input-nickname", "Test" );
+ $this->click( "prefcontrol" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ }
+
+ // Verify change date format
+ public function testChangeDateFormatTimeZone() {
+
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+
+ $this->click( SeleniumTestConstants::LINK_START."My preferences" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+ $this->click( SeleniumTestConstants::LINK_START."Date and time" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ $this->click( "mw-input-date-dmy" );
+ $this->select( "mw-input-timecorrection", "label=Asia/Colombo" );
+ $this->click( "prefcontrol" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify Date format and time zome saved
+ $this->assertEquals( "Your preferences have been saved.",
+ $this->getText( "//div[@id='bodyContent']/div[4]/strong/p" ));
+ }
+
+ // Verify restoring all default settings
+ public function testSetAllDefault() {
+
+ $this->open( $this->getUrl() .
+ '/index.php?title=Main_Page&action=edit' );
+ $this->click( SeleniumTestConstants::LINK_START."My preferences" );
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify restoring all default settings
+ $this->assertEquals( "Restore all default settings",
+ $this->getText( SeleniumTestConstants::LINK_START."Restore all default settings" ));
+
+ $this->click("//*[@id='preferences']/div/a");
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify 'This can not be undone' warning message displayed
+ $this->assertTrue($this->isElementPresent("//input[@value='Restore all default settings']"));
+
+ // Verify 'Restore all default settings' button available
+ $this->assertEquals("You can use this page to reset your preferences to the site defaults. This cannot be undone.",
+ $this->getText("//div[@id='bodyContent']/p"));
+
+ $this->click("//input[@value='Restore all default settings']");
+ $this->waitForPageToLoad( SeleniumTestConstants::WIKI_TEST_WAIT_TIME );
+
+ // Verify preferences saved successfully
+ $this->assertEquals("Your preferences have been saved.",
+ $this->getText("//div[@id='bodyContent']/div[4]/strong/p"));
+ }
+}
+
diff --git a/tests/testHelpers.inc b/tests/testHelpers.inc
new file mode 100644
index 00000000..5d56e625
--- /dev/null
+++ b/tests/testHelpers.inc
@@ -0,0 +1,536 @@
+<?php
+
+/**
+ * @ingroup Testing
+ *
+ * Set of classes to help with test output and such. Right now pretty specific
+ * to the parser tests but could be more useful one day :)
+ *
+ * @todo Fixme: Make this more generic
+ */
+
+class AnsiTermColorer {
+ function __construct() {
+ }
+
+ /**
+ * Return ANSI terminal escape code for changing text attribs/color
+ *
+ * @param $color String: semicolon-separated list of attribute/color codes
+ * @return String
+ */
+ public function color( $color ) {
+ global $wgCommandLineDarkBg;
+
+ $light = $wgCommandLineDarkBg ? "1;" : "0;";
+
+ return "\x1b[{$light}{$color}m";
+ }
+
+ /**
+ * Return ANSI terminal escape code for restoring default text attributes
+ *
+ * @return String
+ */
+ public function reset() {
+ return $this->color( 0 );
+ }
+}
+
+/* A colour-less terminal */
+class DummyTermColorer {
+ public function color( $color ) {
+ return '';
+ }
+
+ public function reset() {
+ return '';
+ }
+}
+
+class TestRecorder {
+ var $parent;
+ var $term;
+
+ function __construct( $parent ) {
+ $this->parent = $parent;
+ $this->term = $parent->term;
+ }
+
+ function start() {
+ $this->total = 0;
+ $this->success = 0;
+ }
+
+ function record( $test, $result ) {
+ $this->total++;
+ $this->success += ( $result ? 1 : 0 );
+ }
+
+ function end() {
+ // dummy
+ }
+
+ function report() {
+ if ( $this->total > 0 ) {
+ $this->reportPercentage( $this->success, $this->total );
+ } else {
+ throw new MWException( "No tests found.\n" );
+ }
+ }
+
+ function reportPercentage( $success, $total ) {
+ $ratio = wfPercent( 100 * $success / $total );
+ print $this->term->color( 1 ) . "Passed $success of $total tests ($ratio)... ";
+
+ if ( $success == $total ) {
+ print $this->term->color( 32 ) . "ALL TESTS PASSED!";
+ } else {
+ $failed = $total - $success ;
+ print $this->term->color( 31 ) . "$failed tests failed!";
+ }
+
+ print $this->term->reset() . "\n";
+
+ return ( $success == $total );
+ }
+}
+
+class DbTestPreviewer extends TestRecorder {
+ protected $lb; // /< Database load balancer
+ protected $db; // /< Database connection to the main DB
+ protected $curRun; // /< run ID number for the current run
+ protected $prevRun; // /< run ID number for the previous run, if any
+ protected $results; // /< Result array
+
+ /**
+ * This should be called before the table prefix is changed
+ */
+ function __construct( $parent ) {
+ parent::__construct( $parent );
+
+ $this->lb = wfGetLBFactory()->newMainLB();
+ // This connection will have the wiki's table prefix, not parsertest_
+ $this->db = $this->lb->getConnection( DB_MASTER );
+ }
+
+ /**
+ * Set up result recording; insert a record for the run with the date
+ * and all that fun stuff
+ */
+ function start() {
+ parent::start();
+
+ if ( ! $this->db->tableExists( 'testrun' )
+ or ! $this->db->tableExists( 'testitem' ) )
+ {
+ print "WARNING> `testrun` table not found in database.\n";
+ $this->prevRun = false;
+ } else {
+ // We'll make comparisons against the previous run later...
+ $this->prevRun = $this->db->selectField( 'testrun', 'MAX(tr_id)' );
+ }
+
+ $this->results = array();
+ }
+
+ function record( $test, $result ) {
+ parent::record( $test, $result );
+ $this->results[$test] = $result;
+ }
+
+ function report() {
+ if ( $this->prevRun ) {
+ // f = fail, p = pass, n = nonexistent
+ // codes show before then after
+ $table = array(
+ 'fp' => 'previously failing test(s) now PASSING! :)',
+ 'pn' => 'previously PASSING test(s) removed o_O',
+ 'np' => 'new PASSING test(s) :)',
+
+ 'pf' => 'previously passing test(s) now FAILING! :(',
+ 'fn' => 'previously FAILING test(s) removed O_o',
+ 'nf' => 'new FAILING test(s) :(',
+ 'ff' => 'still FAILING test(s) :(',
+ );
+
+ $prevResults = array();
+
+ $res = $this->db->select( 'testitem', array( 'ti_name', 'ti_success' ),
+ array( 'ti_run' => $this->prevRun ), __METHOD__ );
+
+ foreach ( $res as $row ) {
+ if ( !$this->parent->regex
+ || preg_match( "/{$this->parent->regex}/i", $row->ti_name ) )
+ {
+ $prevResults[$row->ti_name] = $row->ti_success;
+ }
+ }
+
+ $combined = array_keys( $this->results + $prevResults );
+
+ # Determine breakdown by change type
+ $breakdown = array();
+ foreach ( $combined as $test ) {
+ if ( !isset( $prevResults[$test] ) ) {
+ $before = 'n';
+ } elseif ( $prevResults[$test] == 1 ) {
+ $before = 'p';
+ } else /* if ( $prevResults[$test] == 0 )*/ {
+ $before = 'f';
+ }
+
+ if ( !isset( $this->results[$test] ) ) {
+ $after = 'n';
+ } elseif ( $this->results[$test] == 1 ) {
+ $after = 'p';
+ } else /*if ( $this->results[$test] == 0 ) */ {
+ $after = 'f';
+ }
+
+ $code = $before . $after;
+
+ if ( isset( $table[$code] ) ) {
+ $breakdown[$code][$test] = $this->getTestStatusInfo( $test, $after );
+ }
+ }
+
+ # Write out results
+ foreach ( $table as $code => $label ) {
+ if ( !empty( $breakdown[$code] ) ) {
+ $count = count( $breakdown[$code] );
+ printf( "\n%4d %s\n", $count, $label );
+
+ foreach ( $breakdown[$code] as $differing_test_name => $statusInfo ) {
+ print " * $differing_test_name [$statusInfo]\n";
+ }
+ }
+ }
+ } else {
+ print "No previous test runs to compare against.\n";
+ }
+
+ print "\n";
+ parent::report();
+ }
+
+ /**
+ * Returns a string giving information about when a test last had a status change.
+ * Could help to track down when regressions were introduced, as distinct from tests
+ * which have never passed (which are more change requests than regressions).
+ */
+ private function getTestStatusInfo( $testname, $after ) {
+ // If we're looking at a test that has just been removed, then say when it first appeared.
+ if ( $after == 'n' ) {
+ $changedRun = $this->db->selectField ( 'testitem',
+ 'MIN(ti_run)',
+ array( 'ti_name' => $testname ),
+ __METHOD__ );
+ $appear = $this->db->selectRow ( 'testrun',
+ array( 'tr_date', 'tr_mw_version' ),
+ array( 'tr_id' => $changedRun ),
+ __METHOD__ );
+
+ return "First recorded appearance: "
+ . date( "d-M-Y H:i:s", strtotime ( $appear->tr_date ) )
+ . ", " . $appear->tr_mw_version;
+ }
+
+ // Otherwise, this test has previous recorded results.
+ // See when this test last had a different result to what we're seeing now.
+ $conds = array(
+ 'ti_name' => $testname,
+ 'ti_success' => ( $after == 'f' ? "1" : "0" ) );
+
+ if ( $this->curRun ) {
+ $conds[] = "ti_run != " . $this->db->addQuotes ( $this->curRun );
+ }
+
+ $changedRun = $this->db->selectField ( 'testitem', 'MAX(ti_run)', $conds, __METHOD__ );
+
+ // If no record of ever having had a different result.
+ if ( is_null ( $changedRun ) ) {
+ if ( $after == "f" ) {
+ return "Has never passed";
+ } else {
+ return "Has never failed";
+ }
+ }
+
+ // Otherwise, we're looking at a test whose status has changed.
+ // (i.e. it used to work, but now doesn't; or used to fail, but is now fixed.)
+ // In this situation, give as much info as we can as to when it changed status.
+ $pre = $this->db->selectRow ( 'testrun',
+ array( 'tr_date', 'tr_mw_version' ),
+ array( 'tr_id' => $changedRun ),
+ __METHOD__ );
+ $post = $this->db->selectRow ( 'testrun',
+ array( 'tr_date', 'tr_mw_version' ),
+ array( "tr_id > " . $this->db->addQuotes ( $changedRun ) ),
+ __METHOD__,
+ array( "LIMIT" => 1, "ORDER BY" => 'tr_id' )
+ );
+
+ if ( $post ) {
+ $postDate = date( "d-M-Y H:i:s", strtotime ( $post->tr_date ) ) . ", {$post->tr_mw_version}";
+ } else {
+ $postDate = 'now';
+ }
+
+ return ( $after == "f" ? "Introduced" : "Fixed" ) . " between "
+ . date( "d-M-Y H:i:s", strtotime ( $pre->tr_date ) ) . ", " . $pre->tr_mw_version
+ . " and $postDate";
+
+ }
+
+ /**
+ * Commit transaction and clean up for result recording
+ */
+ function end() {
+ $this->lb->commitMasterChanges();
+ $this->lb->closeAll();
+ parent::end();
+ }
+
+}
+
+class DbTestRecorder extends DbTestPreviewer {
+ var $version;
+
+ /**
+ * Set up result recording; insert a record for the run with the date
+ * and all that fun stuff
+ */
+ function start() {
+ $this->db->begin();
+
+ if ( ! $this->db->tableExists( 'testrun' )
+ or ! $this->db->tableExists( 'testitem' ) )
+ {
+ print "WARNING> `testrun` table not found in database. Trying to create table.\n";
+ $this->db->sourceFile( $this->db->patchPath( 'patch-testrun.sql' ) );
+ echo "OK, resuming.\n";
+ }
+
+ parent::start();
+
+ $this->db->insert( 'testrun',
+ array(
+ 'tr_date' => $this->db->timestamp(),
+ 'tr_mw_version' => $this->version,
+ 'tr_php_version' => phpversion(),
+ 'tr_db_version' => $this->db->getServerVersion(),
+ 'tr_uname' => php_uname()
+ ),
+ __METHOD__ );
+ if ( $this->db->getType() === 'postgres' ) {
+ $this->curRun = $this->db->currentSequenceValue( 'testrun_id_seq' );
+ } else {
+ $this->curRun = $this->db->insertId();
+ }
+ }
+
+ /**
+ * Record an individual test item's success or failure to the db
+ *
+ * @param $test String
+ * @param $result Boolean
+ */
+ function record( $test, $result ) {
+ parent::record( $test, $result );
+
+ $this->db->insert( 'testitem',
+ array(
+ 'ti_run' => $this->curRun,
+ 'ti_name' => $test,
+ 'ti_success' => $result ? 1 : 0,
+ ),
+ __METHOD__ );
+ }
+}
+
+class TestFileIterator implements Iterator {
+ private $file;
+ private $fh;
+ private $parserTest; /* An instance of ParserTest (parserTests.php) or MediaWikiParserTest (phpunit) */
+ private $index = 0;
+ private $test;
+ private $lineNum;
+ private $eof;
+
+ function __construct( $file, $parserTest ) {
+ global $IP;
+
+ $this->file = $file;
+ $this->fh = fopen( $this->file, "rt" );
+
+ if ( !$this->fh ) {
+ throw new MWException( "Couldn't open file '$file'\n" );
+ }
+
+ $this->parserTest = $parserTest;
+ $this->parserTest->showRunFile( wfRelativePath( $this->file, $IP ) );
+
+ $this->lineNum = $this->index = 0;
+ }
+
+ function rewind() {
+ if ( fseek( $this->fh, 0 ) ) {
+ throw new MWException( "Couldn't fseek to the start of '$this->file'\n" );
+ }
+
+ $this->index = -1;
+ $this->lineNum = 0;
+ $this->eof = false;
+ $this->next();
+
+ return true;
+ }
+
+ function current() {
+ return $this->test;
+ }
+
+ function key() {
+ return $this->index;
+ }
+
+ function next() {
+ if ( $this->readNextTest() ) {
+ $this->index++;
+ return true;
+ } else {
+ $this->eof = true;
+ }
+ }
+
+ function valid() {
+ return $this->eof != true;
+ }
+
+ function readNextTest() {
+ $data = array();
+ $section = null;
+
+ while ( false !== ( $line = fgets( $this->fh ) ) ) {
+ $this->lineNum++;
+ $matches = array();
+
+ if ( preg_match( '/^!!\s*(\w+)/', $line, $matches ) ) {
+ $section = strtolower( $matches[1] );
+
+ if ( $section == 'endarticle' ) {
+ if ( !isset( $data['text'] ) ) {
+ throw new MWException( "'endarticle' without 'text' at line {$this->lineNum} of $this->file\n" );
+ }
+
+ if ( !isset( $data['article'] ) ) {
+ throw new MWException( "'endarticle' without 'article' at line {$this->lineNum} of $this->file\n" );
+ }
+
+ $this->parserTest->addArticle( ParserTest::chomp( $data['article'] ), $data['text'], $this->lineNum );
+
+ $data = array();
+ $section = null;
+
+ continue;
+ }
+
+ if ( $section == 'endhooks' ) {
+ if ( !isset( $data['hooks'] ) ) {
+ throw new MWException( "'endhooks' without 'hooks' at line {$this->lineNum} of $this->file\n" );
+ }
+
+ foreach ( explode( "\n", $data['hooks'] ) as $line ) {
+ $line = trim( $line );
+
+ if ( $line ) {
+ if ( !$this->parserTest->requireHook( $line ) ) {
+ return false;
+ }
+ }
+ }
+
+ $data = array();
+ $section = null;
+
+ continue;
+ }
+
+ if ( $section == 'endfunctionhooks' ) {
+ if ( !isset( $data['functionhooks'] ) ) {
+ throw new MWException( "'endfunctionhooks' without 'functionhooks' at line {$this->lineNum} of $this->file\n" );
+ }
+
+ foreach ( explode( "\n", $data['functionhooks'] ) as $line ) {
+ $line = trim( $line );
+
+ if ( $line ) {
+ if ( !$this->parserTest->requireFunctionHook( $line ) ) {
+ return false;
+ }
+ }
+ }
+
+ $data = array();
+ $section = null;
+
+ continue;
+ }
+
+ if ( $section == 'end' ) {
+ if ( !isset( $data['test'] ) ) {
+ throw new MWException( "'end' without 'test' at line {$this->lineNum} of $this->file\n" );
+ }
+
+ if ( !isset( $data['input'] ) ) {
+ throw new MWException( "'end' without 'input' at line {$this->lineNum} of $this->file\n" );
+ }
+
+ if ( !isset( $data['result'] ) ) {
+ throw new MWException( "'end' without 'result' at line {$this->lineNum} of $this->file\n" );
+ }
+
+ if ( !isset( $data['options'] ) ) {
+ $data['options'] = '';
+ }
+
+ if ( !isset( $data['config'] ) )
+ $data['config'] = '';
+
+ if ( ( ( preg_match( '/\\bdisabled\\b/i', $data['options'] ) && !$this->parserTest->runDisabled )
+ || !preg_match( "/" . $this->parserTest->regex . "/i", $data['test'] ) ) ) {
+ # disabled test
+ $data = array();
+ $section = null;
+
+ continue;
+ }
+
+ $this->test = array(
+ 'test' => ParserTest::chomp( $data['test'] ),
+ 'input' => ParserTest::chomp( $data['input'] ),
+ 'result' => ParserTest::chomp( $data['result'] ),
+ 'options' => ParserTest::chomp( $data['options'] ),
+ 'config' => ParserTest::chomp( $data['config'] ) );
+
+ return true;
+ }
+
+ if ( isset ( $data[$section] ) ) {
+ throw new MWException( "duplicate section '$section' at line {$this->lineNum} of $this->file\n" );
+ }
+
+ $data[$section] = '';
+
+ continue;
+ }
+
+ if ( $section ) {
+ $data[$section] .= $line;
+ }
+ }
+
+ return false;
+ }
+}