From: jenkins-bot Date: Thu, 1 Dec 2016 10:49:35 +0000 (+0000) Subject: Merge "Align search result CSS with Wikimedia UI color palette" X-Git-Tag: 1.31.0-rc.0~4705 X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=commitdiff_plain;h=11cdbc02482f7b9de42184f02f1484dc1ff48cbd;hp=8249ed5ff87e98f880f0b56faf8198299e9ceb72 Merge "Align search result CSS with Wikimedia UI color palette" --- diff --git a/RELEASE-NOTES-1.29 b/RELEASE-NOTES-1.29 index ab52544fc6..21a94c5d4a 100644 --- a/RELEASE-NOTES-1.29 +++ b/RELEASE-NOTES-1.29 @@ -56,6 +56,8 @@ changes to languages because of Phabricator reports. SearchEngineFactory::getSearchEngineClass() instead. * $wgSessionsInMemcached (deprecated in 1.20) was removed. No replacement is required as all sessions are stored in Object Cache now. +* MWHttpRequest::execute() should be considered to return a StatusValue; the + Status return type is deprecated. == Compatibility == diff --git a/includes/PrefixSearch.php b/includes/PrefixSearch.php index f6c4147118..04c17e4021 100644 --- a/includes/PrefixSearch.php +++ b/includes/PrefixSearch.php @@ -239,7 +239,7 @@ abstract class PrefixSearch { // canonical and alias title forms... $keys = []; foreach ( SpecialPageFactory::getNames() as $page ) { - $keys[$wgContLang->caseFold( $page )] = $page; + $keys[$wgContLang->caseFold( $page )] = [ 'page' => $page, 'rank' => 0 ]; } foreach ( $wgContLang->getSpecialPageAliases() as $page => $aliases ) { @@ -247,33 +247,35 @@ abstract class PrefixSearch { continue; } - foreach ( $aliases as $alias ) { - $keys[$wgContLang->caseFold( $alias )] = $alias; + foreach ( $aliases as $key => $alias ) { + $keys[$wgContLang->caseFold( $alias )] = [ 'page' => $alias, 'rank' => $key ]; } } ksort( $keys ); - $srchres = []; - $skipped = 0; + $matches = []; foreach ( $keys as $pageKey => $page ) { if ( $searchKey === '' || strpos( $pageKey, $searchKey ) === 0 ) { // bug 27671: Don't use SpecialPage::getTitleFor() here because it // localizes its input leading to searches for e.g. Special:All // returning Spezial:MediaWiki-Systemnachrichten and returning // Spezial:Alle_Seiten twice when $wgLanguageCode == 'de' - if ( $offset > 0 && $skipped < $offset ) { - $skipped++; - continue; + $matches[$page['rank']][] = Title::makeTitleSafe( NS_SPECIAL, $page['page'] ); + + if ( isset( $matches[0] ) && count( $matches[0] ) >= $limit + $offset ) { + // We have enough items in primary rank, no use to continue + break; } - $srchres[] = Title::makeTitleSafe( NS_SPECIAL, $page ); } - if ( count( $srchres ) >= $limit ) { - break; - } } - return $srchres; + // Ensure keys are in order + ksort( $matches ); + // Flatten the array + $matches = array_reduce( $matches, 'array_merge', [] ); + + return array_slice( $matches, $offset, $limit ); } /** diff --git a/includes/http/CurlHttpRequest.php b/includes/http/CurlHttpRequest.php index f58c3a9a5b..7fd3e835c4 100644 --- a/includes/http/CurlHttpRequest.php +++ b/includes/http/CurlHttpRequest.php @@ -38,11 +38,10 @@ class CurlHttpRequest extends MWHttpRequest { } public function execute() { - - parent::execute(); + $this->prepare(); if ( !$this->status->isOK() ) { - return $this->status; + return Status::wrap( $this->status ); // TODO B/C; move this to callers } $this->curlOptions[CURLOPT_PROXY] = $this->proxy; @@ -102,7 +101,7 @@ class CurlHttpRequest extends MWHttpRequest { $curlHandle = curl_init( $this->url ); if ( !curl_setopt_array( $curlHandle, $this->curlOptions ) ) { - throw new MWException( "Error setting curl options." ); + throw new InvalidArgumentException( "Error setting curl options." ); } if ( $this->followRedirects && $this->canFollowRedirects() ) { @@ -140,7 +139,7 @@ class CurlHttpRequest extends MWHttpRequest { $this->parseHeader(); $this->setStatus(); - return $this->status; + return Status::wrap( $this->status ); // TODO B/C; move this to callers } /** diff --git a/includes/http/Http.php b/includes/http/Http.php index 43ae2d0e8f..a68a63b7f0 100644 --- a/includes/http/Http.php +++ b/includes/http/Http.php @@ -74,7 +74,7 @@ class Http { } else { $errors = $status->getErrorsByType( 'error' ); $logger = LoggerFactory::getInstance( 'http' ); - $logger->warning( $status->getWikiText( false, false, 'en' ), + $logger->warning( Status::wrap( $status )->getWikiText( false, false, 'en' ), [ 'error' => $errors, 'caller' => $caller, 'content' => $req->getContent() ] ); return false; } diff --git a/includes/http/MWHttpRequest.php b/includes/http/MWHttpRequest.php index 08883ae44f..a42b6d0991 100644 --- a/includes/http/MWHttpRequest.php +++ b/includes/http/MWHttpRequest.php @@ -46,9 +46,11 @@ class MWHttpRequest implements LoggerAwareInterface { protected $reqHeaders = []; protected $url; protected $parsedUrl; + /** @var callable */ protected $callback; protected $maxRedirects = 5; protected $followRedirects = false; + protected $connectTimeout; /** * @var CookieJar @@ -60,7 +62,8 @@ class MWHttpRequest implements LoggerAwareInterface { protected $respStatus = "200 Ok"; protected $respHeaders = []; - public $status; + /** @var StatusValue */ + protected $status; /** * @var Profiler @@ -98,9 +101,9 @@ class MWHttpRequest implements LoggerAwareInterface { } if ( !$this->parsedUrl || !Http::isValidURI( $this->url ) ) { - $this->status = Status::newFatal( 'http-invalid-url', $url ); + $this->status = StatusValue::newFatal( 'http-invalid-url', $url ); } else { - $this->status = Status::newGood( 100 ); // continue + $this->status = StatusValue::newGood( 100 ); // continue } if ( isset( $options['timeout'] ) && $options['timeout'] != 'default' ) { @@ -161,7 +164,7 @@ class MWHttpRequest implements LoggerAwareInterface { * @param string $url Url to use * @param array $options (optional) extra params to pass (see Http::request()) * @param string $caller The method making this request, for profiling - * @throws MWException + * @throws DomainException * @return CurlHttpRequest|PhpHttpRequest * @see MWHttpRequest::__construct */ @@ -169,7 +172,7 @@ class MWHttpRequest implements LoggerAwareInterface { if ( !Http::$httpEngine ) { Http::$httpEngine = function_exists( 'curl_init' ) ? 'curl' : 'php'; } elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) { - throw new MWException( __METHOD__ . ': curl (http://php.net/curl) is not installed, but' . + throw new DomainException( __METHOD__ . ': curl (http://php.net/curl) is not installed, but' . ' Http::$httpEngine is set to "curl"' ); } @@ -186,7 +189,7 @@ class MWHttpRequest implements LoggerAwareInterface { return new CurlHttpRequest( $url, $options, $caller, Profiler::instance() ); case 'php': if ( !wfIniGetBool( 'allow_url_fopen' ) ) { - throw new MWException( __METHOD__ . ': allow_url_fopen ' . + throw new DomainException( __METHOD__ . ': allow_url_fopen ' . 'needs to be enabled for pure PHP http requests to ' . 'work. If possible, curl should be used instead. See ' . 'http://php.net/curl.' @@ -194,7 +197,7 @@ class MWHttpRequest implements LoggerAwareInterface { } return new PhpHttpRequest( $url, $options, $caller, Profiler::instance() ); default: - throw new MWException( __METHOD__ . ': The setting of Http::$httpEngine is not valid.' ); + throw new DomainException( __METHOD__ . ': The setting of Http::$httpEngine is not valid.' ); } } @@ -222,7 +225,7 @@ class MWHttpRequest implements LoggerAwareInterface { * * @return void */ - public function proxySetup() { + protected function proxySetup() { // If there is an explicit proxy set and proxies are not disabled, then use it if ( $this->proxy && !$this->noProxy ) { return; @@ -300,7 +303,7 @@ class MWHttpRequest implements LoggerAwareInterface { * Get an array of the headers * @return array */ - public function getHeaderList() { + protected function getHeaderList() { $list = []; if ( $this->cookieJar ) { @@ -333,12 +336,14 @@ class MWHttpRequest implements LoggerAwareInterface { * bytes are reported handled than were passed to you, the HTTP fetch * will be aborted. * - * @param callable $callback - * @throws MWException + * @param callable|null $callback + * @throws InvalidArgumentException */ public function setCallback( $callback ) { - if ( !is_callable( $callback ) ) { - throw new MWException( 'Invalid MwHttpRequest callback' ); + if ( is_null( $callback ) ) { + $callback = [ $this, 'read' ]; + } elseif ( !is_callable( $callback ) ) { + throw new InvalidArgumentException( __METHOD__ . ': invalid callback' ); } $this->callback = $callback; } @@ -350,6 +355,7 @@ class MWHttpRequest implements LoggerAwareInterface { * @param resource $fh * @param string $content * @return int + * @internal */ public function read( $fh, $content ) { $this->content .= $content; @@ -359,9 +365,14 @@ class MWHttpRequest implements LoggerAwareInterface { /** * Take care of whatever is necessary to perform the URI request. * - * @return Status + * @return StatusValue + * @note currently returns Status for B/C */ public function execute() { + throw new LogicException( 'children must override this' ); + } + + protected function prepare() { $this->content = ""; if ( strtoupper( $this->method ) == "HEAD" ) { @@ -371,7 +382,7 @@ class MWHttpRequest implements LoggerAwareInterface { $this->proxySetup(); // set up any proxy as needed if ( !$this->callback ) { - $this->setCallback( [ $this, 'read' ] ); + $this->setCallback( null ); } if ( !isset( $this->reqHeaders['User-Agent'] ) ) { @@ -494,6 +505,8 @@ class MWHttpRequest implements LoggerAwareInterface { /** * Tells the MWHttpRequest object to use this pre-loaded CookieJar. * + * To read response cookies from the jar, getCookieJar must be called first. + * * @param CookieJar $jar */ public function setCookieJar( $jar ) { @@ -519,14 +532,18 @@ class MWHttpRequest implements LoggerAwareInterface { * Set-Cookie headers. * @see Cookie::set * @param string $name - * @param mixed $value + * @param string $value * @param array $attr */ - public function setCookie( $name, $value = null, $attr = null ) { + public function setCookie( $name, $value, $attr = [] ) { if ( !$this->cookieJar ) { $this->cookieJar = new CookieJar; } + if ( $this->parsedUrl && !isset( $attr['domain'] ) ) { + $attr['domain'] = $this->parsedUrl['host']; + } + $this->cookieJar->setCookie( $name, $value, $attr ); } diff --git a/includes/http/PhpHttpRequest.php b/includes/http/PhpHttpRequest.php index 2af000fac0..d8a9949d2f 100644 --- a/includes/http/PhpHttpRequest.php +++ b/includes/http/PhpHttpRequest.php @@ -87,6 +87,7 @@ class PhpHttpRequest extends MWHttpRequest { * is completely useless (something like "fopen: failed to open stream") * so normal methods of handling errors programmatically * like get_last_error() don't work. + * @internal */ public function errorHandler( $errno, $errstr ) { $n = count( $this->fopenErrors ) + 1; @@ -94,8 +95,7 @@ class PhpHttpRequest extends MWHttpRequest { } public function execute() { - - parent::execute(); + $this->prepare(); if ( is_array( $this->postData ) ) { $this->postData = wfArrayToCgi( $this->postData ); @@ -227,12 +227,12 @@ class PhpHttpRequest extends MWHttpRequest { . ': error opening connection: {errstr1}', $this->fopenErrors ); } $this->status->fatal( 'http-request-error' ); - return $this->status; + return Status::wrap( $this->status ); // TODO B/C; move this to callers } if ( $result['timed_out'] ) { $this->status->fatal( 'http-timed-out', $this->url ); - return $this->status; + return Status::wrap( $this->status ); // TODO B/C; move this to callers } // If everything went OK, or we received some error code @@ -253,6 +253,6 @@ class PhpHttpRequest extends MWHttpRequest { } fclose( $fh ); - return $this->status; + return Status::wrap( $this->status ); // TODO B/C; move this to callers } } diff --git a/includes/installer/DatabaseUpdater.php b/includes/installer/DatabaseUpdater.php index 6a702e9fbb..6a8a99ff09 100644 --- a/includes/installer/DatabaseUpdater.php +++ b/includes/installer/DatabaseUpdater.php @@ -32,8 +32,6 @@ require_once __DIR__ . '/../../maintenance/Maintenance.php'; * @since 1.17 */ abstract class DatabaseUpdater { - protected static $updateCounter = 0; - /** * Array of updates to perform on the database * @@ -423,8 +421,6 @@ abstract class DatabaseUpdater { * @param array $what What updates to perform */ public function doUpdates( $what = [ 'core', 'extensions', 'stats' ] ) { - global $wgVersion; - $this->db->setSchemaVars( $this->getSchemaVars() ); $what = array_flip( $what ); @@ -441,12 +437,9 @@ abstract class DatabaseUpdater { $this->checkStats(); } - $this->setAppliedUpdates( $wgVersion, $this->updates ); - if ( $this->fileHandle ) { $this->skipSchema = false; $this->writeSchemaUpdateFile(); - $this->setAppliedUpdates( "$wgVersion-schema", $this->updatesSkipped ); } } @@ -482,23 +475,6 @@ abstract class DatabaseUpdater { $this->updates = array_merge( $this->updates, $updatesDone ); } - /** - * @param string $version - * @param array $updates - */ - protected function setAppliedUpdates( $version, $updates = [] ) { - $this->db->clearFlag( DBO_DDLMODE ); - if ( !$this->canUseNewUpdatelog() ) { - return; - } - $key = "updatelist-$version-" . time() . self::$updateCounter; - self::$updateCounter++; - $this->db->insert( 'updatelog', - [ 'ul_key' => $key, 'ul_value' => serialize( $updates ) ], - __METHOD__ ); - $this->db->setFlag( DBO_DDLMODE ); - } - /** * Helper function: check if the given key is present in the updatelog table. * Obviously, only use this for updates that occur after the updatelog table was diff --git a/includes/libs/CookieJar.php b/includes/libs/CookieJar.php index 910a7ca82d..8f5700abb9 100644 --- a/includes/libs/CookieJar.php +++ b/includes/libs/CookieJar.php @@ -19,7 +19,11 @@ * @ingroup HTTP */ +/** + * Cookie jar to use with MWHttpRequest. Does not handle cookie unsetting. + */ class CookieJar { + /** @var Cookie[] */ private $cookie = []; /** diff --git a/includes/specials/SpecialBrokenRedirects.php b/includes/specials/SpecialBrokenRedirects.php index 8927fbf58f..b730ecd789 100644 --- a/includes/specials/SpecialBrokenRedirects.php +++ b/includes/specials/SpecialBrokenRedirects.php @@ -136,13 +136,7 @@ class BrokenRedirectsPage extends QueryPage { [ 'action' => 'edit' ] ); } - $to = $linkRenderer->makeLink( - $toObj, - null, - [], - [], - [ 'broken' ] - ); + $to = $linkRenderer->makeBrokenLink( $toObj ); $arr = $this->getLanguage()->getArrow(); $out = $from . $this->msg( 'word-separator' )->escaped(); diff --git a/languages/i18n/azb.json b/languages/i18n/azb.json index f1469056f8..f3bbf8352a 100644 --- a/languages/i18n/azb.json +++ b/languages/i18n/azb.json @@ -1962,6 +1962,7 @@ "whatlinkshere-hidelinks": "$1 باغلانتیلاری", "whatlinkshere-hideimages": "فایل باغلانتیلارینی $1", "whatlinkshere-filters": "سۆزگَجلر", + "whatlinkshere-submit": "گئت", "autoblockid": "اوتوماتیک باغلانما #$1", "block": "ایستیفادچینی باغلاما", "unblock": "ایستیفاده‌چی‌نین باغلانماسین گؤتور", diff --git a/languages/i18n/be-tarask.json b/languages/i18n/be-tarask.json index 73e2397807..ba40fdf7d6 100644 --- a/languages/i18n/be-tarask.json +++ b/languages/i18n/be-tarask.json @@ -2629,6 +2629,7 @@ "pageinfo-category-pages": "Колькасьць старонак", "pageinfo-category-subcats": "Колькасьць падкатэгорыяў", "pageinfo-category-files": "Колькасьць файлаў", + "pageinfo-user-id": "Ідэнтыфікатар удзельніка", "markaspatrolleddiff": "Пазначыць як «патруляваную»", "markaspatrolledtext": "Пазначыць гэтую старонку як «патруляваную»", "markaspatrolledtext-file": "Пазначыць гэтую вэрсію файлу як патруляваную", diff --git a/languages/i18n/diq.json b/languages/i18n/diq.json index 110d9b5c5e..fe9e024eb2 100644 --- a/languages/i18n/diq.json +++ b/languages/i18n/diq.json @@ -85,7 +85,7 @@ "tuesday": "Sêşeme", "wednesday": "Çarşeme", "thursday": "Pancşeme", - "friday": "Yene", + "friday": "Êne", "saturday": "Şeme", "sun": "Krê", "mon": "Dış", @@ -170,7 +170,7 @@ "morenotlisted": "Na lista qay kemi ya.", "mypage": "Pele", "mytalk": "Mesac", - "anontalk": "Werênayış", + "anontalk": "Vaten", "navigation": "Pusula", "and": " u", "qbfind": "Bıvêne", @@ -196,7 +196,7 @@ "history_short": "Tarix", "updatedmarker": "cıkewtena mına peyêne ra dıme biyo rocane", "printableversion": "Asayışê çapkerdışi", - "permalink": "Gıreyo daimi", + "permalink": "Gıreyo bêpeyni", "print": "Çap ke", "view": "Bıvêne", "view-foreign": "$1 de bıvêne", @@ -253,19 +253,19 @@ "pool-servererror": "Amordoğa xızmeti ya istifade nëbena $1", "poolcounter-usage-error": "Xırab karyayış:$1", "aboutsite": "Heqa {{SITENAME}} de", - "aboutpage": "Project:Heqa", + "aboutpage": "Proce:Heqa", "copyright": "Zerrekacı $1 bındı not biya.", "copyrightpage": "{{ns:project}}:Heqa telifi", "currentevents": "Hediseyê rocaneyi", "currentevents-url": "Project:Hediseyê rocaneyi", "disclaimers": "Redê mesuliyeti", - "disclaimerpage": "Project:Reddê mesuliyetê bıngey", + "disclaimerpage": "Project:Redê mesulêtê pêroyi", "edithelp": "Peştdariya vurnayışi", "helppage-top-gethelp": "Peşti", "mainpage": "Pela Seri", "mainpage-description": "Pela seri", "policy-url": "Project:Terzê hereketi", - "portal": "Meydanê cemaeti", + "portal": "Portalê cemaeti", "portal-url": "Project:Portalë şëlıgi", "privacy": "Politikaya nımıteyiye", "privacypage": "Project:Xısusiyetê nımıtışi", @@ -291,7 +291,7 @@ "editlink": "bıvurne", "viewsourcelink": "çımey bıvêne", "editsectionhint": "Leteyo ke bıvuriyo: $1", - "toc": "Sernameyê meselan", + "toc": "Tedeestey", "showtoc": "bımocne", "hidetoc": "bınımne", "collapsible-collapse": "Teng kı", @@ -317,14 +317,14 @@ "nstab-main": "Pele", "nstab-user": "Pella karberi", "nstab-media": "Pela medya", - "nstab-special": "Pella xısusi", + "nstab-special": "Pela xısusiye", "nstab-project": "Pela proceyi", "nstab-image": "Dosya", "nstab-mediawiki": "Mesac", "nstab-template": "Şablon", "nstab-help": "Pela peşti", "nstab-category": "Kategoriye", - "mainpage-nstab": "Pera esas", + "mainpage-nstab": "Pela seri", "nosuchaction": "Fealiyeto wınasi çıniyo", "nosuchactiontext": "URL ra kar qebul nêbı.\nŞıma belka URL şaş nuşt, ya zi gıreyi şaş ra ameyi.\nKeyepelê {{SITENAME}} eşkeno xeta eşkera bıkero.", "nosuchspecialpage": "Pella xısusi ya unasin çınya", @@ -367,7 +367,7 @@ "perfcached": "Datay cı ver hazır biye. No semedê ra nıkayin niyo! tewr zaf {{PLURAL:$1|netice|$1 netice}} debêno de", "perfcachedts": "Cêr de malumatê nımıteyi esti, demdê newe kerdışo peyın: $1. Tewr zaf {{PLURAL:$4|netice|$4 neticey cı}} debyayo de", "querypage-no-updates": "Rocanebiyayışê na pele nıka cadayiyê.\nDayiyi tiya nıka newe nêbenê.", - "viewsource": "Çemi bıvin", + "viewsource": "Çımey bıvêne", "viewsource-title": "Cı geyrayışê $1'i bıvin", "actionthrottled": "Kerden peysnaya", "actionthrottledtext": "Riyê tedbirê anti-spami ra, wextê do kılmek de şıma nê fealiyeti nêşkenê zaf zêde bıkerê, şıma ki no hedi viyarna ra.\nÇend deqey ra tepeya reyna bıcerrebnên.", @@ -604,13 +604,13 @@ "summary": "Xulasa:", "subject": "Mewzu:", "minoredit": "No yew vurnayışo werdiyo", - "watchthis": "Ena pele bıewne", - "savearticle": "Peller qeyd kı", + "watchthis": "Bıewni ena perrer", + "savearticle": "Perrer qeyd kı", "savechanges": "Vuryayışa qeyd kerê", "publishpage": "Perer bıhesırne", "publishchanges": "Vurnayışa vıla ke", "preview": "Verqayt", - "showpreview": "Var asani bıvinê", + "showpreview": "Ver asayışi bıvinê", "showdiff": "Vurriyayışa bıasne", "anoneditwarning": "İqaz: Şıma be hesabê xo nêkewtê cı. \nAdresê şımayê IP tarixê vırnayışê na pele de do qeyd bo. Eke şıma [$1 cıkewê] ya zi [$2 hesab vırazê], vurnayışê şıma be zewbina kare ra nameyê şıma rê bar beno.", "anonpreviewwarning": "\"Şıma be hesabê xo nêkewtê cı. Eke qeyd kerê, adresê şımaê IP tarixê vırnayışê na pele de do qeyd bo.\"", @@ -744,7 +744,7 @@ "nohistory": "Verê vurnayışanê na pele çıniyo.", "currentrev": "Çımraviyarnayışo rocane", "currentrev-asof": "$1 ra tepya mewcud weziyeta pela", - "revisionasof": "Verziyonê roca $1ine", + "revisionasof": "Çımraviyarnayışê $1", "revision-info": "Vurnayışo ke $1 de terefê {{GENDER:$6|$2}}$7 ra biyo", "previousrevision": "← Çımraviyarnayışo kıhanêr", "nextrevision": "Rewizyono newên →", @@ -864,13 +864,13 @@ "lineno": "Xeta $1:", "compareselectedversions": "Rewizyonanê weçineyan pêver ke", "showhideselectedversions": "Revizyonanê weçinıtan bımocne/bınımne", - "editundo": "peyser biya", + "editundo": "Peyser bıgêre", "diff-empty": "(Babetna niyo)", "diff-multi-sameuser": "(Terefê eyni karberi ra {{PLURAL:$1|yew revizyono miyanên nêmocno|$1 revizyonê miyanêni nêmocnê}})", "diff-multi-otherusers": "(Terefê {{PLURAL:$2|yew karberi|$2 karberan}} ra {{PLURAL:$1|yew revizyono miyanên nêmocno|$1 revizyonê miyanêni nêmocnê}})", "diff-multi-manyusers": "({{PLURAL:$1|jew timar kerdışo qıckeko|$1 timar kerdışo qıckeko}} timar kerdo, $2 {{PLURAL:$2|Karber|karberi}} memocne)", "difference-missing-revision": "Ferqê {{PLURAL:$2|Yew rewizyonê|$2 rewizyonê}} {{PLURAL:$2|dı|dı}} ($1) sero çıniyo.\n\nNo normal de werênayış dê pelanê besterneyan dı ena xırabin asena.\nDetayê besternayışi [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} tiya dı] aseno.", - "searchresults": "Neticeyê geyrayışi", + "searchresults": "Peyniyê cıgeyrayışi", "searchresults-title": "Qandê \"$1\" neticeyê geyrayışi", "titlematches": "Tekê (zewcê) sernameyê pele", "textmatches": "Tekê (zewcê) nuştey pele", @@ -881,17 +881,17 @@ "next-page": "Pela peyên", "prevn-title": "$1o verên {{PLURAL:$1|netice|neticeyan}}", "nextn-title": "$1o ke yeno {{PLURAL:$1|netice|neticey}}", - "shown-title": "Herg per sero $1 {{PLURAL:$1|netici|netica}} bıasne", + "shown-title": "Her pele sero $1 {{PLURAL:$1|netici|netica}} bımocne", "viewprevnext": "($1 {{int:pipe-separator}} $2) ($3) bıvênên", "searchmenu-exists": "''Ena 'Wikipediya de ser \"[[:$1]]\" yew pel esto'''", "searchmenu-new": "Na wiki de pela \"[[:$1]]\" vıraze! {{PLURAL:$2|0=|Sewbina pela ke şıma geyrayê cı aye bıvênê.|Yew zi neticanê cıgeyrayışê xo bıvênê.}}", - "searchprofile-articles": "Perrê muhteway", + "searchprofile-articles": "Pelê zerreki", "searchprofile-images": "Zafınmedya", "searchprofile-everything": "Pêro çi", "searchprofile-advanced": "Herayen", "searchprofile-articles-tooltip": "$1 de cı geyre", "searchprofile-images-tooltip": "Dosya cı geyre", - "searchprofile-everything-tooltip": "Tedeesteyan hemine cı geyre (pelanê mınaqeşeyi zi tey)", + "searchprofile-everything-tooltip": "Tedeesteyan hemine cı geyre (pelanê werênayışi zi tey)", "searchprofile-advanced-tooltip": "Cayê nameyanê xısusiyan de cı geyre", "search-result-size": "$1 ({{PLURAL:$2|1 çeku|$2 çekuy}})", "search-result-category-size": "{{PLURAL:$1|1 eza|$1 ezayan}} ({{PLURAL:$2|1 kategoriyê bini|$2 kategirayanê binan}}, {{PLURAL:$3|1 dosya|$3 dosyayan}})", @@ -1093,7 +1093,7 @@ "right-bot": "Zey yew kardê otomotiki kar bıvin", "right-nominornewtalk": "Pelanê werênayışan rê vurnayışê qıckeki çıniyê, qutiya mesacanê newiyan bıgurene", "right-apihighlimits": "Persanê API de sinoranê berzêran bıgurene", - "right-writeapi": "İstıfadey APIyê nuştey", + "right-writeapi": "Gurenayışê nuştey API", "right-delete": "Pele bestere", "right-bigdelete": "Pelanê be tarixanê dergan bestere", "right-deletelogentry": "Qeydanê cıkewtışanê xısusiyan bestere û peyser biya", @@ -1223,12 +1223,12 @@ "nchanges": "$1 {{PLURAL:$1|vurnayış|vurnayışi}}", "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|ziyaretê peyêni ra nata}}", "enhancedrc-history": "tarix", - "recentchanges": "Vurriyayışê peyêni", + "recentchanges": "Vuriyayışê peyêni", "recentchanges-legend": "Tercihê vurnayışanê peyênan", "recentchanges-summary": "Wiki sero vurriyayışê peyêni asenê.", "recentchanges-noresult": "Goreyê kriteranê kıfşkerdeyan ra qet yew vurnayış nêvêniya.", "recentchanges-feed-description": "Ena feed dı vurnayişanê tewr peniyan teqip bık.", - "recentchanges-label-newpage": "Enê vurnayışi ra yu pera newi vıraziya ya", + "recentchanges-label-newpage": "Enê vurnayışi yew pela newiye vıraşta.", "recentchanges-label-minor": "No yew vurnayışo werdiyo", "recentchanges-label-bot": "Eno vurnayış terefê yew boti ra vıraziyo", "recentchanges-label-unpatrolled": "Eno vurnayış hewna dewriya nêbiyo", @@ -1254,7 +1254,7 @@ "rcshowhidepatr": "$1 vurnayışê ke dewriya geyrayê", "rcshowhidepatr-show": "Bımocne", "rcshowhidepatr-hide": "Bınımne", - "rcshowhidemine": "vurnayışanê mı $1", + "rcshowhidemine": "vurnayışê mı $1", "rcshowhidemine-show": "Bımocne", "rcshowhidemine-hide": "Bınımne", "rcshowhidecategorization": "kategorizasyonê pele $1", @@ -1273,7 +1273,7 @@ "rc_categories": "Kategoriyan rêz kı ( \"|“ ya ciya yo):", "rc_categories_any": "Weçinayiyan ra her yew", "rc-change-size": "$1", - "rc-change-size-new": "Vurnayışa dıma $1 {{PLURAL:$1|bayt|bayt}}", + "rc-change-size-new": "$1 {{PLURAL:$1|bayt|bayt}} ra dıma vurnayış", "newsectionsummary": "/* $1 */ qısımo newe", "rc-enhanced-expand": "Detaya bıvin (JavaScript lazımo)", "rc-enhanced-hide": "Melumat bınımne", @@ -1664,7 +1664,7 @@ "listusers-desc": "Kemeyen rézed ratn", "usereditcount": "$1 {{PLURAL:$1|vurnayîş|vurnayîşî}}", "usercreated": "$2 de $1 {{GENDER:$3|viraziya}}", - "newpages": "Perrê newey", + "newpages": "Pelê newey", "newpages-submit": "Bımocne", "newpages-username": "Nameyê karberi:", "ancientpages": "Perrê kı rewnayo kı nêvuriya yê", @@ -1894,7 +1894,7 @@ "delete-warning-toobig": "no pel wayirê tarixê vurnayiş ê derg o, $1 {{PLURAL:$1|revizyonê|revizyonê}} seri de.\nhewn a kerdışê ıney {{SITENAME}} şuxul bıne gırano;\nbı diqqet dewam kerê.", "deleteprotected": "Şıma nêşenê ena perer esternê, çıkı per starya ya.", "rollback": "vurnayişan tepiya bıger", - "rollbacklink": "peyser biya", + "rollbacklink": "peyser biyare", "rollbacklinkcount": "$1 {{PLURAL:$1|vurnayış|vurnayışi}} peyd gıroti", "rollbacklinkcount-morethan": "$1 {{PLURAL:$1|vurnayış|vuranyışi}} tewr peyd gırot", "rollbackfailed": "Peyserardış nêbi", @@ -2031,7 +2031,7 @@ "sp-contributions-deleted": "iştırakê {{GENDER:$1|karberi}} esterdi", "sp-contributions-uploads": "Barkerdışi", "sp-contributions-logs": "qeydi", - "sp-contributions-talk": "werênayış", + "sp-contributions-talk": "vaten", "sp-contributions-userrights": "idareyê heqanê karberan", "sp-contributions-blocked-notice": "verniyê no/na karber/e geriyayo/a\nqê referansi qeydê vernigrewtışi cêr de eşkera biyo:", "sp-contributions-blocked-notice-anon": "Eno adresê IPi bloke biyo.\nCıkewtışo tewr peyêno ke bloke biyo, cêr seba referansi belikerdeyo:", @@ -2337,7 +2337,7 @@ "tooltip-pt-createaccount": "Şıma rê tewsiyey ma xorê jew hesab akerê. Fına zi hesab akerdış mecburi niyo.", "tooltip-ca-talk": "Heqa zerrekê pele de werênayış", "tooltip-ca-edit": "Ena pele bıvurne", - "tooltip-ca-addsection": "Zu bınnusteya newi ak", + "tooltip-ca-addsection": "Yew leteyo newe a ke", "tooltip-ca-viewsource": "Ena pele kılit biya.\nŞıma şenê çımeyê aye bıvênê", "tooltip-ca-history": "Versiyonê verênê ena pele", "tooltip-ca-protect": "Ena pele bışevekne", @@ -2373,7 +2373,7 @@ "tooltip-ca-nstab-media": "Pela medya bıvêne", "tooltip-ca-nstab-special": "Na yew pela xasa, şıma nêşenê sero vurnayış bıkerê", "tooltip-ca-nstab-project": "Pela proceyi bıvêne", - "tooltip-ca-nstab-image": "Pera dosyayer bıvin", + "tooltip-ca-nstab-image": "Pela dosya bıvêne", "tooltip-ca-nstab-mediawiki": "Mesacê sistemi bımocne", "tooltip-ca-nstab-template": "Şabloni bıvêne", "tooltip-ca-nstab-help": "Pela peşti bıvêne", @@ -2381,7 +2381,7 @@ "tooltip-minoredit": "Nay vırnayışa werdi nışan bıkeré", "tooltip-save": "Vurnayışanê xo qeyd ke", "tooltip-publish": "Vurnayışê xo vıla kı", - "tooltip-preview": "Vuryayışané xo çım ra ravyarné. Verdé qeyd kerdışi eneri bıkarné!", + "tooltip-preview": "Vurnayışanê xo çım ra bıviyarnê. Qeydkerdış ra ver bıgurê cı!", "tooltip-diff": "Metni sero vurnayışan mocneno", "tooltip-compareselectedversions": "Ena per de ferqê rewziyonan de dı weçinaya bıvinê", "tooltip-watch": "Ena pele lista xoya seyrkerdışi ke", @@ -2392,7 +2392,7 @@ "tooltip-rollback": "\"Peyser biya\" be yew tık pela iştıraqanê peyênan peyser ano", "tooltip-undo": "\"Undo\" ena vurnayışê newi iptal kena u vurnayışê verni a kena.\nTı eşkeno yew sebeb bınus.", "tooltip-preferences-save": "Terciha qeyd ke", - "tooltip-summary": "Yew xulasaya kilm binuse", + "tooltip-summary": "Yew xulasa kılmeke bınuse", "interlanguage-link-title": "$1 - $2", "common.css": "/************************************************\n * COMMON CSS\n *\n * Any CSS placed in this page will be used on \n * all skins, please think carefully about if it\n * belongs here (and not in one of the skin CSS\n * pages) before adding it. Thanks.\n ************************************************/\n\n/* */\ntable.highlighthovertable tr:hover,\ntable.highlighthovertable tr:hover td,\ntable.mw-ext-translate-groupstatistics tr:hover,\ntable.mw-ext-translate-groupstatistics tr:hover td {\n background-color: white;\n}\n\n\n/* Babel wrapper layout. */\n/* XXX: This is either redundant or should be in-core */\n/* @noflip */table.mw-babel-wrapper {\n\twidth: 238px;\n\tfloat: right;\n\tclear: right;\n\tmargin: 1em;\n\tborder-style: solid;\n\tborder-width: 1px;\n\tborder-color: #99B3FF;\n}\n\n/* Babel box layout. */\n/* @noflip */div.mw-babel-box {\n\tfloat: left;\n\tclear: left;\n\tmargin: 1px;\n}\n\ndiv.mw-babel-box table {\n\twidth: 238px;\n}\n\ndiv.mw-babel-box table th {\n\twidth: 238px;\n\twidth: 45px;\n\theight: 45px;\n\tfont-size: 14pt;\n\tfont-family: monospace;\n}\n\ndiv.mw-babel-box table td {\n\tfont-size: 8pt;\n\tpadding: 4pt;\n\tline-height: 1.25em;\n}\n\n/* Babel box colours. */\ndiv.mw-babel-box-0 {\n\tborder: solid #B7B7B7 1px;\n}\n\ndiv.mw-babel-box-1 {\n\tborder: solid #C0C8FF 1px;\n}\n\ndiv.mw-babel-box-2 {\n\tborder: solid #77E0E8 1px;\n}\n\ndiv.mw-babel-box-3 {\n\tborder: solid #99B3FF 1px;\n}\n\ndiv.mw-babel-box-4 {\n\tborder: solid #CCCC00 1px;\n}\n\ndiv.mw-babel-box-5 {\n\tborder: solid #F99C99 1px;\n}\n\ndiv.mw-babel-box-N {\n\tborder: solid #6EF7A7 1px;\n}\n\ndiv.mw-babel-box-0 table th {\n\tbackground-color: #B7B7B7;\n}\n\ndiv.mw-babel-box-1 table th {\n\tbackground-color: #C0C8FF;\n}\n\ndiv.mw-babel-box-2 table th {\n\tbackground-color: #77E0E8;\n}\n\ndiv.mw-babel-box-3 table th {\n\tbackground-color: #99B3FF;\n}\n\ndiv.mw-babel-box-4 table th {\n\tbackground-color: #CCCC00;\n}\n\ndiv.mw-babel-box-5 table th {\n\tbackground-color: #F99C99;\n}\n\ndiv.mw-babel-box-N table th{\n\tbackground-color: #6EF7A7;\n}\n\ndiv.mw-babel-box-0 table {\n\tbackground-color: #E8E8E8;\n}\n\ndiv.mw-babel-box-1 table {\n\tbackground-color: #F0F8FF;\n}\n\ndiv.mw-babel-box-2 table {\n\tbackground-color: #D0F8FF;\n}\n\ndiv.mw-babel-box-3 table {\n\tbackground-color: #E0E8FF;\n}\n\ndiv.mw-babel-box-4 table {\n\tbackground-color: #FFFF99;\n}\n\ndiv.mw-babel-box-5 table {\n\tbackground-color: #F9CBC9;\n}\n\ndiv.mw-babel-box-N table {\n\tbackground-color: #C5FCDC;\n}\n\n.babel-box td.babel-footer {\n\ttext-align: center;\n}\n\n/* Styling for portals. */\ndiv.table {\n display: table;\n vertical-align: top;\n width: 100%;\n}\n\ndiv.table-row {\n display: table-row;\n vertical-align: top;\n}\n\ndiv.table-cell {\n display: table-cell;\n vertical-align: top;\n}\n\nbody.ns-100 table.mw-babel-wrapper {\n border: solid 1px #bbbbbb;\n background-color: #f0f0f0;\n margin-left: 1em;\n}\n\n.graytext {\n color: #aaa;\n}\n\n/* On [[Special:RecentChanges]] and [[Special:Watchlist]] make the new pages symbol bold green and the minor edit symbol gray. */\n.newpage {\n color: green;\n font-weight: bold\n}\n\n.minoredit,\n.minor {\n color: gray;\n}\n\n/* Monospace diffs, this makes more sense since diffs show what would be seen in the edit box. */\n/* Note: Anno 2012 many browsers don't use monospace in the textarea anymore by default, notably Chrome and Safari don't (unless the user overrides this in the preferences) */\n.diff-context,\n.diff-deletedline,\n.diff-addedline {\n font-family: monospace, \"Courier New\";\n/* Just guess does the stupid wikidiff2 extensions add extra whitespace around..... */\n white-space: -moz-pre-wrap;\n white-space: pre-wrap;\n}\n \n.diffchange {\n border: 1px dotted rgb( 170, 170, 170 );\n}\n\n/* It is unclear what the following CSS does, please add comments if you can clarify. */\n/* The box which is 400px high and if its content is longer, it gets the scrollbar */\n.scrollme {\n overflow: scroll;\n width: 100%;\n height: 400px;\n}\n\n/* Standard Navigationsleisten, aka box hiding thingy from .de. Documentation at [[Wikipedia:NavFrame]]. */\ndiv.Boxmerge, div.NavFrame { margin: 0; padding: 4px; border-collapse: collapse;}\ndiv.Boxmerge div.NavFrame { border-style: none; border-style: hidden; }\ndiv.NavFrame + div.NavFrame { border-top-style: none; border-top-style: hidden; }\ndiv.NavFrame div.NavHead { height: 1.6em; position:relative; }\ndiv.NavEnd { margin: 0; padding: 0; line-height: 1px; clear: both; }\na.NavToggle { position: absolute; top: 0; right: 5px; }\n.note-flaggedrevs * a.NavToggle { right: 12px; } /* For [[Template:Flagged Revs]] */\n\n/* Template:Languages */\n.bw-languages {\n border: 1px solid #aaaaaa;\n padding: 0.2em;\n border-collapse: collapse;\n line-height: 1.2;\n font-size: 95%;\n margin: 1px 1px;\n}\n.bw-languages-title {\n width: 180px;\n border: 1px solid #aaaaaa;\n background: #EEF3E2;\n padding: 0.5em;\n font-weight: bold;\n}\n.bw-languages-links { padding:0.5em; background:#F6F9ED; }\n\n/* Senseless in this project */\n#editpage-copywarn { display: none; }\n\n/* Hide warnings about bad links on MediaWiki:Common.css */\n.page-MediaWiki_Common_css .mw-translate-messagechecks { display: none; }\n\n/*******************\n** Faciliate RTL translation\n*******************/\n/* @noflip */\n#bodyContent .arabic a {\n\tpadding-right:0;\n\tbackground:none;\n}\n\n.vatop tr, tr.vatop, .vatop td, .vatop th {\n vertical-align: top;\n}\n\n.bw-languages {\n direction: ltr;\n}\n\n/* prevent wrapping of lines in LQT TOC if not necessary */\ntable.lqt_toc {\n\twidth: auto;\n}\n\n/* [[m:MediaZilla:35337]] */\n@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 1.5dppx) {\n #p-logo a {\n background-image: url(\"//translatewiki.net/images/thumb/7/7c/Translatewiki-logo-bare.svg/152px-Translatewiki-logo-bare.svg.png\") !important;\n background-size: auto 135px;\n }\n}\n@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) {\n #p-logo a {\n background-image: url(\"//translatewiki.net/images/thumb/7/7c/Translatewiki-logo-bare.svg/202px-Translatewiki-logo-bare.svg.png\") !important;\n background-size: auto 135px;\n }\n}\n\n/* qqq visibility, [[Thread:Support/Suggestion: Add this CSS to MediaWiki:Common.css]] */\n \n.mw-sp-translate-edit-info .mw-content-ltr {\n background-position:left center;\n padding-left:45px;\n}\nfieldset.mw-sp-translate-edit-info .mw-centent-rtl {\n background-position:right center;\n padding-right:45px;\n}\n\n/* Semantic MediaWiki - make special properties easier to identify */\n\n.smwbuiltin a,\n.smwbuiltin a.new {\n\tcolor: #FF8000;\n}\n\n/* Recentchangestext toggle link */\n.white-link a {\n color: #fff;\n}", "common.js": "/* Any JavaScript here will be loaded for all users on every page load. */", @@ -2491,14 +2491,14 @@ "widthheight": "$1 - $2", "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|pele|peli}}", "file-info": "ebatê dosyayi: $1, MIME tip: $2", - "file-info-size": "$1 × $2 pixelan, ebatê dosya: $3, MIME type: $4", + "file-info-size": "$1 × $2 pikselan, ebatê dosya: $3, MIME tipê cı: $4", "file-info-size-pages": "$1 × $2 pikse, dergeya dosyay: $3, MIME tipiya cı: $4, $5 {{PLURAL:$5|pela|pela}}", "file-nohires": "Deha berz agozney cı çıniyo", "svg-long-desc": "Dosyay SVG, zek vanê $1 × $2 piksela, ebatê dosya: $3", "svg-long-desc-animated": "SVG dosya, nominalin $1 × $2 piksela, ebatê dosya: $3", "svg-long-error": "Nêmeqbul dosyaya SVG'i: $1", "show-big-image": "Dosyaya oricinale", - "show-big-image-preview": "Verqaytê dergiya: $1.", + "show-big-image-preview": "Vervênayışê ebatê : $1.", "show-big-image-other": "Zewmi{{PLURAL:$2|Vılêşnayış|Vılêşnayışê}}: $1.", "show-big-image-size": "$1 × $2 piksel", "file-info-gif-looped": "viyariye biyo", @@ -3184,7 +3184,7 @@ "fileduplicatesearch-result-1": "Dosyayê ''$1î'' de hem-kopya çini yo.", "fileduplicatesearch-result-n": "Dosyayê ''$1î'' de {{PLURAL:$2|1 hem-kopya|$2 hem-kopyayî'}} esto.", "fileduplicatesearch-noresults": "Ebe namey \"$1\" ra dosya nêdiyayê.", - "specialpages": "Pellê xısusiy", + "specialpages": "Perrê Hısusi", "specialpages-note-top": "Kıtabek", "specialpages-note": "* Pelê xasê normali.\n* Pelê xasê nımıtey.", "specialpages-group-maintenance": "Raporê pawıtışi", diff --git a/languages/i18n/fi.json b/languages/i18n/fi.json index 8cf7922f25..0ebbd472df 100644 --- a/languages/i18n/fi.json +++ b/languages/i18n/fi.json @@ -1127,12 +1127,9 @@ "userrights-reason": "Syy:", "userrights-no-interwiki": "Sinulla ei ole oikeutta muokata käyttöoikeuksia muissa wikeissä.", "userrights-nodatabase": "Tietokantaa $1 ei ole tai se ei ole paikallinen.", - "userrights-nologin": "Sinun täytyy [[Special:UserLogin|kirjautua sisään]] ylläpitäjän tunnuksella, jotta voisit muuttaa käyttöoikeuksia.", - "userrights-notallowed": "Sinulla ei ole oikeutta lisätä tai poistaa käyttäjien oikeuksia.", "userrights-changeable-col": "Ryhmät, joita voit muuttaa", "userrights-unchangeable-col": "Ryhmät, joita et voi muuttaa", "userrights-conflict": "Päällekkäinen käyttöoikeuksien muutos! Tarkista tekemäsi muutokset ja vahvista ne.", - "userrights-removed-self": "Poistit omat oikeutesi. Tämän vuoksi sinulla ei enää ole oikeutta päästä tälle sivulle.", "group": "Ryhmä", "group-user": "käyttäjät", "group-autoconfirmed": "automaattisesti hyväksytyt käyttäjät", @@ -1840,6 +1837,8 @@ "apisandbox-alert-field": "Tässä kentässä oleva arvo ei ole kelvollinen.", "apisandbox-continue": "Jatka", "apisandbox-continue-clear": "Tyhjennä", + "apisandbox-multivalue-all-namespaces": "$1 (Kaikki nimiavaruudet)", + "apisandbox-multivalue-all-values": "$1 (Kaikki arvot)", "booksources": "Kirjalähteet", "booksources-search-legend": "Etsi kirjalähteitä", "booksources-isbn": "ISBN", @@ -2069,7 +2068,7 @@ "changecontentmodel-legend": "Muuta sisältömallia", "changecontentmodel-title-label": "Sivun otsikko", "changecontentmodel-model-label": "Uusi sisältömalli", - "changecontentmodel-reason-label": "Syy:", + "changecontentmodel-reason-label": "Syy", "changecontentmodel-submit": "Tee muutos", "changecontentmodel-success-title": "Sisältömallia on muutettu", "changecontentmodel-success-text": "Sisältötyyppiä kohteessa [[:$1]] on muutettu.", @@ -3696,6 +3695,7 @@ "mw-widgets-dateinput-placeholder-month": "VVVV-KK", "mw-widgets-titleinput-description-new-page": "sivua ei ole olemassa vielä", "mw-widgets-titleinput-description-redirect": "ohjaus kohteeseen $1", + "mw-widgets-categoryselector-add-category-placeholder": "Lisää luokka...", "sessionmanager-tie": "!!FYZZ!!Cannot combine multiple request authentication types: $1.", "sessionprovider-generic": "$1 istuntoa", "sessionprovider-mediawiki-session-cookiesessionprovider": "istuntoja, joissa on evästeet käytössä", @@ -3820,7 +3820,5 @@ "unlinkaccounts-success": "Tunnuksen linkitys poistettiin.", "authenticationdatachange-ignored": "Varmennustietojen muutosta ei käsitelty. Ehkä palveluntarjoajaa ei määritelty?", "restrictionsfield-badip": "Virheellinen IP-osoite tai alue: $1", - "restrictionsfield-label": "Sallitut IP-alueet:", - "edit-error-short": "$1", - "edit-error-long": "Virheet:\n\n$1" + "restrictionsfield-label": "Sallitut IP-alueet:" } diff --git a/languages/i18n/hy.json b/languages/i18n/hy.json index ea3fd1e6f8..a4120c9430 100644 --- a/languages/i18n/hy.json +++ b/languages/i18n/hy.json @@ -354,7 +354,7 @@ "cannotdelete": "Չհաջողվեց ջնջել «$1» էջը կամ ֆայլը։\nՀավանաբար այն արդեն ջնջվել է մեկ այլ մասնակցի կողմից։", "cannotdelete-title": "Հնարավոր չէ ջնջել $1 էջը", "delete-hook-aborted": "Խմբագրել չեղյալ է.\nԼրացուցիչ պարզաբանումներ չի դրվել.", - "no-null-revision": "Չի հաջողվել ստեղծել նոր զրոյական правку համար էջը \"$1\"", + "no-null-revision": "Չի հաջողվել ստեղծել նոր զրոյական խմբագրում էջի համար \"$1\"", "badtitle": "Անընդունելի անվանում", "badtitletext": "Հարցված էջի անվանումը անընդունելի է, դատարկ է կամ սխալ միջ-լեզվական կամ ինտերվիքի անվանում է։ Հնարավոր է, որ այն պարունակում է անթույլատրելի սիմվոլներ։", "title-invalid-empty": "Էջի հայցվող վերնագիրը դատարկ է կամ պարունակում է միայն անվանատարածքի անունը։", diff --git a/languages/i18n/mr.json b/languages/i18n/mr.json index 6ef89e66cf..1596eb6236 100644 --- a/languages/i18n/mr.json +++ b/languages/i18n/mr.json @@ -206,7 +206,7 @@ "namespaces": "नामविश्वे", "variants": "चले(व्हेरियंट्स)", "navigation-heading": "दिक्चालन यादी", - "errorpagetitle": "चूक", + "errorpagetitle": "त्रुटी", "returnto": "$1 कडे परत चला.", "tagline": "{{SITENAME}} कडून", "help": "साहाय्य", @@ -1809,6 +1809,7 @@ "listgrants-rights": "अधिकार", "trackingcategories": "मागोवा घेणारे वर्ग", "trackingcategories-summary": "या पानात ते रेखापथनातील वर्ग(tracking categories) आहेत, जे, मिडियाविकि संचेतनाद्वारे स्वयंचलितरित्या वसविण्यात (तयार करण्यात) आले आहेत. त्यांची नावे, {{ns:8}} नामविश्वातील संबंधित प्रणाली संदेशात फेरफार करुन, बदलविता येतात.", + "trackingcategories-msg": "मागोवा घेणारा वर्ग", "trackingcategories-name": "संदेश नाम", "trackingcategories-desc": "वर्ग अंतर्भूत करण्याचे निकष", "trackingcategories-nodesc": "वर्णन उपलब्ध नाही.", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index 03458d13dd..936fd8b8ac 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -1201,7 +1201,7 @@ "search-external": "Legend of the fieldset for the input form when the internal search is disabled. Inside the fieldset [[MediaWiki:Searchdisabled]] and [[MediaWiki:Googlesearch]] is shown.", "searchdisabled": "{{doc-singularthey}}\nIn this sentence, \"their indexes\" refers to \"Google's indexes\".\n\nShown on [[Special:Search]] when the internal search is disabled.", "googlesearch": "{{notranslate}}\nShown when [[mw:Manual:$wgDisableTextSearch|$wgDisableTextSearch]] is set to true and no [[mw:Manual:$wgSearchForwardUrl|$wgSearchForwardUrl]] is set.\n\nParameters:\n* $1 - the search term\n* $2 - \"UTF-8\" (hard-coded)\n* $3 - the message {{msg-mw|Searchbutton}}", - "search-error": "Shown when an error has occurred when performing a search. Parameters:\n* $1 - the localized error that was returned.", + "search-error": "Shown when an error has occurred when performing a search. Parameters:\n* $1 - the localized error that was returned", "search-warning": "Shown when a warning has occured when performing a search. Parameters:\n* $1 - the localized warning that was returned.", "opensearch-desc": "{{ignored}}Link description of the [www.opensearch.org/ OpenSearch] link in the HTML head of pages.", "preferences": "Title of the [[Special:Preferences]] page.\n{{Identical|Preferences}}", diff --git a/languages/i18n/roa-tara.json b/languages/i18n/roa-tara.json index deeece5c76..807ced9781 100644 --- a/languages/i18n/roa-tara.json +++ b/languages/i18n/roa-tara.json @@ -375,7 +375,7 @@ "mypreferencesprotected": "Non ge tìne le permesse pe cangià le preferenze tune.", "ns-specialprotected": "Le pàgene speciale no ponne essere cangete.", "titleprotected": "Stu titele ha state prutette da 'a ccreazione da [[User:$1|$1]].\n'U mutive jè $2.", - "filereadonlyerror": "Non ge pozze cangià 'u file \"$1\" purcé l'archivije de le file \"$2\" ste in mode sola letture.\n\nL'amministratore ca l'ha bloccate dèje sta spiegazione: \"$3\".", + "filereadonlyerror": "Non ge pozze cangià 'u file \"$1\" purcé l'archivije de le file \"$2\" ste in sola letture.\n\nL'amministratore d'u sisteme ca l'ave bloccate dèje sta spiegazione: \"$3\".", "invalidtitle-knownnamespace": "Titole invalide cu 'u namespace \"$2\" e teste \"$3\"", "invalidtitle-unknownnamespace": "Titele invalide cu numere de namespace scanusciute $1 e teste \"$2\"", "exception-nologin": "Non ge sì collegate", @@ -451,7 +451,7 @@ "noname": "Non gìè specifichete 'nu nome utende valide.", "loginsuccesstitle": "Tutte a poste, è trasute!", "loginsuccess": "'''Mò tu si colleghete jndr'à {{SITENAME}} cumme \"$1\".'''", - "nosuchuser": "Non g'esiste n'utende cu 'u nome \"$1\".\nFà attenzione ca le nome de l'utinde so senzibbele a le lettere granne e piccenne.\nVide bbuene a cumme l'è scritte, o [[Special:CreateAccount|ccreje n'utende nuève]].", + "nosuchuser": "Non g'esiste n'utende cu 'u nome \"$1\".\nLe nome de l'utinde so senzibbele a le lettere granne e piccenne.\nVide bbuene a cumme l'è scritte, o [[Special:CreateAccount|ccreje n'utende nuève]].", "nosuchusershort": "Non ge ste nisciune utende cu 'u nome \"$1\".\nCondrolle accume l'è scritte.", "nouserspecified": "A scrivere pe forze 'u nome de l'utende.", "login-userblocked": "Stu utende jè bloccate. Non ge puè trasè.", @@ -467,7 +467,7 @@ "noemail": "Non ge stonne email reggistrete pe l'utende \"$1\".", "noemailcreate": "Tu ha mèttere 'n'indirizze e-mail valide", "passwordsent": "'Na nova passuord ha state mannete a l'indirizze e-mail reggistrete pe \"$1\".\nPe piacere, colleghete n'otra vota quanne l'è ricevute.", - "blocked-mailpassword": "L'indirizze IP tue jè blocchete pe le cangiaminde e accussì tu non ge puè ausà 'a funzione de recupere d'a password pe prevenìe l'abbuse.", + "blocked-mailpassword": "L'indirizze IP tune jè bloccate pe le cangiaminde. Tu non ge puè ausà 'a funzione de recupere d'a password pe prevenìe l'abbuse.", "eauthentsent": "'N'e-mail de conferme ha state mannate a l'indirizze ca tu è ditte.\nApprime ca otre e-mail avènene mannate a 'u cunde tune, tu ha seguì le 'struzione ca stonne jndr'à l'e-mail, pe confermà ca 'u cunde jè une de le tune.", "throttled-mailpassword": "'Nu arrecordatore de passuord ha stete già mannate jndr'à {{PLURAL:$1|l'urtema ore|l'urteme $1 ore}}.\nPe prevenì l'abbuse, sulamende 'nu arrecordatore de passuord avene mannate ogne {{PLURAL:$1|ore|$1 ore}}.", "mailerror": "Errore mannanne 'a mail: $1", @@ -525,8 +525,6 @@ "passwordreset-emaildisabled": "Le funziune de l'email onne state disabbilitate sus a sta uicchi.", "passwordreset-username": "Nome utende:", "passwordreset-domain": "Dominie:", - "passwordreset-capture": "Vide 'a mail resultande?", - "passwordreset-capture-help": "Ce tu signe sta sckatele, 'a mail (cu 'a passuord temboranèe) t'avène fatte vedè cumme adda essere mannate a l'utende.", "passwordreset-email": "Indirizze e-mail:", "passwordreset-emailtitle": "Dettaglie d'u cunde utende sus a {{SITENAME}}", "passwordreset-emailtext-ip": "Quacchedune (pò essere tu, da 'u 'ndirizze IP $1) ha richieste 'na mail pe arrecurdarse de le dettaglie d'u cunde sue pe {{SITENAME}} ($4). {{PLURAL:$3|'U cunde utende seguende jè|le cunde utinde seguende sonde}} associate cu st'indirizze e-mail:\n\n$2\n\n{{PLURAL:$3|Sta passuord temboranèe scade|Ste passuord temboranèe scadene}} 'mbrà {{PLURAL:$5|'nu sciurne|$5 sciurne}}.\nTu avissa trasè e scacchià 'na passuord nova. Ce quacchedun'otre ha fatte sta richieste, o ce tu t'è arrecurdate 'a passuord origgenale toje, e non g'a vuè ccu cange cchiù, tu puè ignorà stu messagge e condinuà ausanne 'a passuord vecchie.", @@ -1021,13 +1019,10 @@ "userrights-reason": "Mutive:", "userrights-no-interwiki": "Tu non ge tìne le permesse pe cangià le deritte utende sus a l'otre uicchi.", "userrights-nodatabase": "'U Database $1 non g'esiste o non g'è lochele.", - "userrights-nologin": "Tu à essere [[Special:UserLogin|colleghete]] cu 'nu cunde utende d'amministratore pe assignà le deritte utende.", - "userrights-notallowed": "Non ge tìne le permesse pe aggiungere o luà le deritte a le utinde.", "userrights-changeable-col": "Gruppe ca tu puè cangià", "userrights-unchangeable-col": "Gruppe ca tu non ge puè cangià", "userrights-irreversible-marker": "$1*", "userrights-conflict": "Conflitte sus a le cangiaminde de le deritte utende! Pe piacere revide e conferme le cangiaminde tune.", - "userrights-removed-self": "T'è luate le deritte tune. Mò non ge puè cchiù trasè jndr'à sta pàgene.", "group": "Gruppe:", "group-user": "Utinde", "group-autoconfirmed": "Utinde auto confermatarije", @@ -1117,7 +1112,6 @@ "right-siteadmin": "Blocche e sblocche 'u database", "right-override-export-depth": "L'esportazione de pàggene inglude pàggene collegate 'mbonde a 'na profonnetà de 5", "right-sendemail": "Manne 'a mail a otre utinde", - "right-passwordreset": "Vide l'e-mail de azzeramende d'a passuord", "right-managechangetags": "CCreje e scangìlle [[Special:Tags|tag]] da 'u database", "right-applychangetags": "Appleche [[Special:Tags|tag]] sus a 'u de le cangiaminde tune", "right-changetags": "Aggiunge e live arbitrariamende [[Special:Tags|tag]] sus a le revisiune individuale e vôsce de l'archivije", diff --git a/languages/i18n/sah.json b/languages/i18n/sah.json index 6e45545aea..66e53af262 100644 --- a/languages/i18n/sah.json +++ b/languages/i18n/sah.json @@ -1809,6 +1809,10 @@ "apisandbox-alert-field": "Хонуу суолтата алҕастаах.", "apisandbox-continue": "Салгыы", "apisandbox-continue-clear": "Сот", + "apisandbox-continue-help": "{{int:apisandbox-continue}} бүтэһик көрдөбүлү [https://www.mediawiki.org/wiki/API:Query#Continuing_queries салгыаҕа]; {{int:apisandbox-continue-clear}} салҕааһыны кытта ситимнээх туруоруулары ырастыа.", + "apisandbox-param-limit": "Муҥутуур болдьох муҥутуурдук туттулларын туоруор.", + "apisandbox-multivalue-all-namespaces": "$1 (Аат даллара барыта)", + "apisandbox-multivalue-all-values": "$1 (Бары суолталара)", "booksources": "Кинигэлэр источниктара", "booksources-search-legend": "Кинигэ туһунан көрдөө", "booksources-search": "Бул", diff --git a/languages/i18n/udm.json b/languages/i18n/udm.json index 3a5a537d15..75b503cc1e 100644 --- a/languages/i18n/udm.json +++ b/languages/i18n/udm.json @@ -257,6 +257,7 @@ "databaseerror-query": "Курон: $1", "databaseerror-function": "Функция: $1", "databaseerror-error": "Янгыш: $1", + "badtitle": "Умойтэм ним", "badtitletext": "Курем бам ним луэ мыдлань, буш либо кылъёс куспын яке викиос куспын нимыз умойтэм герӟамын.\nНимын, вылды, ярантэм символъёс вань.", "viewsource": "Кодзэ учкыны", "viewsource-title": "Кодзэ учкыны бам $1", @@ -278,6 +279,7 @@ "createacct-another-username-ph": "Учётной книга нимъёс пыртэмын", "yourpassword": "Лушкемкыл:", "userlogin-yourpassword": "Лушкемкыл", + "userlogin-yourpassword-ph": "Гожтэ асьтэлэсь парольдэс", "createacct-yourpassword-ph": "Гожтэ паролез", "createacct-yourpasswordagain": "Пароль юнматэ", "createacct-yourpasswordagain-ph": "Гожтэ паролез эшшо одӥг пол", @@ -291,6 +293,7 @@ "logout": "Кошкыны", "userlogout": "Потыны", "notloggedin": "Тон эн тусбуяськыны сӧзнэтэз", + "userlogin-noaccount": "Ас учётной записьты ӧвӧл?", "nologin": "Учётной книга ӧвӧл-а? $1.", "nologinlink": "Выль вики-авторлэн регистрациез", "createaccount": "выль вики-авторлэн регистрациез", @@ -304,6 +307,9 @@ "createacct-submit": "Выль вики-авторлэн регистрациез", "createacct-another-submit": "Выль вики-авторлэн регистрациез", "createacct-benefit-heading": "{{SITENAME}} — тӥ выллем адямиослэн валче ужамзы.", + "createacct-benefit-body1": "{{PLURAL:$1|тупатон}}", + "createacct-benefit-body2": "{{PLURAL:$1|бам}}", + "createacct-benefit-body3": "{{PLURAL:$1|викиавтор}} берло дыре", "loginerror": "Янгышъёс пырон", "createacct-error": "Янгышъёс бордын учётной книга кылдытыны", "createaccounterror": "Уг быгатиськы гожъян учётной кылдоз: $1", @@ -356,8 +362,10 @@ "editing": "Тупатон: $1", "creating": "«$1» бамез кылдытон", "editingsection": "Тупатон: $1 (люкет)", + "templatesused": "Та бам пушкы пыртэм {{PLURAL:$1|шаблон|шаблонъёс}}:", "template-protected": "(утемын)", "template-semiprotected": "(полуутемын)", + "hiddencategories": "Та бам пыре {{PLURAL:$1|$1 ватэм категорие}}:", "nocreatetext": "Та сайтлэн бамаз выль сюбегатэм луонлыкъёсын кылдытон.\nТон улыса, берлань вуэ быгатэ бам отредактировать, [[Special:UserLogin|тусбуяськыны книгае яке выль система кылдыто учётной]].", "nocreate-loggedin": "Тон доразы юаськыны кылдӥз выль бам ӧвӧл.", "permissionserrors": "Янгышъёс юаське", @@ -368,8 +376,12 @@ "cantcreateaccount-text": "Та книгаез кылдытонлы учётной IP-адрес ($1) заблокировать луизы [[User:$3|$3]].\n\nМугез, вайиз $3 возьматэ $2", "cantcreateaccount-range-text": "Учётной кылдытон - гожъян IP-адрес диапазонын $1, Тон пыриське со IP-адрес ($4), заблокировать луизы [[User:$3|$3]].\n\nМугез, вайиз $3 возьматэ $2", "viewpagelogs": "Та бамлы журналъёсыз возьматыны", + "currentrev-asof": "Алиез версия $1", "revisionasof": "Версия $1", + "revision-info": "Версия $1; {{GENDER:$6|$2}}$7", "previousrevision": "← Вужгем", + "nextrevision": "Выльгем →", + "currentrevisionlink": "Алиез версия", "cur": "али", "last": "азьв.", "history-show-deleted": "Ӵушылэмъёссэ гинэ", @@ -380,6 +392,7 @@ "revdelete-radio-unset": "Адӟымон", "revdelete-reason-dropdown": "*Вӧлскем палэнскон мугъёсты\n** Авторской правоосты тӥян\n** Яке кулэтэм информациез личной комментарий\n** Логин несоответствовать\n** Курла информациез Потенциально", "history-title": "$1 — воштонъёслэн историзы", + "difference-title": "$1 — версиосыз куспын пӧртэмлык", "lineno": "$1-тӥ чур:", "compareselectedversions": "Быръем версиосыз ӵошатыны", "showhideselectedversions": "Возьматыны/ватыны быръем версиосыз", @@ -401,6 +414,7 @@ "search-result-size": "$1 ({{PLURAL:$2|$2 кыл}})", "search-redirect": "($1 бамысь ыстон)", "search-section": "(«$1» люкет)", + "search-suggest": "Тӥ, вылды, утчаллямды «$1».", "search-interwiki-more": "(эшшо)", "searchall": "Ваньзэ", "search-showingresults": "{{PLURAL:$4|$3 пӧлысь $1-тӥ шедьтэм|$3 пӧлысь $1—$2 шедьтэмъёс}}", @@ -447,6 +461,7 @@ "rcshowhideminor-hide": "Ватыны", "rcshowhidebots": "$1 ботъёсыз", "rcshowhidebots-show": "Возьматыны", + "rcshowhidebots-hide": "Ватыны", "rcshowhideliu": "$1 пырем викиавторъёсыз", "rcshowhideliu-show": "Возьматыны", "rcshowhideliu-hide": "Ватыны", @@ -486,6 +501,7 @@ "upload-dialog-button-cancel": "Берытсконо", "license-header": "Лицензия", "nolicense": "Ӧвӧл", + "imgfile": "файл", "file-anchor-link": "Файл", "filehist": "Файллэн историез", "filehist-help": "Зӥбе дата/дыр шоры, кызьы файл со дырын адӟиськемез учкыны вылысь.", @@ -504,11 +520,15 @@ "randompage": "Олокыӵе статья", "withoutinterwiki-submit": "Возьматыны", "nbytes": "{{PLURAL:$1|$1 байт}}", + "nmembers": "$1 {{PLURAL:$1|объект}}", "prefixindex-submit": "Возьматыны", "newpages": "Выль бамъёс", "newpages-submit": "Возьматыны", "move": "Нимзэ воштыны", + "pager-older-n": "{{PLURAL:$1|вужгес $1}}", "booksources": "Книгаосын источникъёс", + "booksources-search-legend": "Книга сярысь информациез утчан", + "booksources-search": "Утчаны", "log": "Журналъёс", "logeventslist-submit": "Возьматыны", "showhideselectedlogentries": "Возьматыны/ватыны быръем журналъёсысь гожъямъёсыз", @@ -516,7 +536,9 @@ "checkbox-all": "Ваньзэ", "checkbox-none": "Номыре", "checkbox-invert": "Воштыны интыен", + "allarticles": "Ваньмыз бамъёс", "allpagessubmit": "Быдэстоно", + "categories": "Категориос", "categories-submit": "Возьматыны", "sp-deletedcontributions-contribs": "тупатонъёсыз", "listusers-submit": "Возьматыны", @@ -535,8 +557,10 @@ "watchlist-options": "Чаклан списокез тупатыны", "enotif_reset": "Вань бамъёсыз лыдӟем пусйыны", "historyaction-submit": "Возьматыны", + "dellogpage": "Быдтонъёсын журнал", "deletionlog": "палэнэ журнал", "rollbacklink": "ӝог берыктыны", + "rollbacklinkcount": "$1 {{PLURAL:$1|тупатонэз}} ӝог берыктыны", "revertpage": "Откат шонертон [[Special:Contributions/$2|$2]] ([[User talk:$2|обсуждение]]) доры версия [[User:$1|$1]]", "revertpage-nouser": "Откат шонертон (пыриськисьёс ватэм нимъёссы) доры версия {{GENDER:$1|[[User:$1|$1]]}}", "restriction-edit": "Тупатон", @@ -549,6 +573,8 @@ "mycontris": "Гожтэмъёс", "anoncontribs": "Гожтэмъёс", "nocontribs": "Критерии нокыӵе воштӥськонъёс та соответствующий шедьтыны уг луы.", + "month": "Толэзьысен (вазен но):", + "year": "Арысен (вазен но):", "sp-contributions-blocklog": "блокировка", "sp-contributions-deleted": "шонертон палэнтыны {{GENDER:$1|участник|куакеч}}", "sp-contributions-blocked-notice": "Пользователь заблокирован сётӥз та учырлы. Справка понна радъяськылӥсь журнал блокировка лапег берпуметӥ гожтэт:", @@ -595,6 +621,7 @@ "block-log-flags-nousertalk": "тупатъяны ачиз уггес быгаты бамлэн обсуждениосаз", "range_block_disabled": "Администратор диапазонэз блокировать али.", "move-watch": "Чаклан списоке пыртоно инъет но валтӥсь бамъёсыз", + "movelogpage": "Нимъёсты воштонъёсын журнал", "export": "Бамъёсты поттон", "allmessagesname": "Ивортон", "allmessages-filter-all": "Ваньзэ", @@ -635,6 +662,7 @@ "tooltip-ca-nstab-main": "Валтӥсь бамез учконо", "tooltip-ca-nstab-user": "Викиавторлэн бамез", "tooltip-ca-nstab-special": "Та бам нимысьтыз, сое тупатон луонтэм", + "tooltip-ca-nstab-project": "Проектлэн бамез", "tooltip-ca-nstab-image": "Файллэн бамез", "tooltip-ca-nstab-template": "Шаблонлэн бамез", "tooltip-ca-nstab-category": "Категорилэн бамез", @@ -648,6 +676,7 @@ "pageinfo-header-edits": "Воштонъёслэн историзы", "pageinfo-toolboxlink": "Бам сярысь тодэтъёс", "previousdiff": "← Вужгес тупатон", + "nextdiff": "Выльгес тупатон →", "file-info-size": "$1 × $2 пиксель, файллэн быдӟалаез: $3, MIME-тип: $4", "file-nohires": "Бадӟымгес быдӟалаен суред ӧвӧл.", "svg-long-desc": "SVG файл, номинально $1 × $2 пиксель, файллэн быдӟалаез: $3", @@ -681,7 +710,9 @@ "tags-title": "Меткаос", "logentry-delete-delete": "$1 {{GENDER:$2|палэнтыны|палэнтыны}} бам $3", "logentry-delete-restore": "$1 {{GENDER:$2|выльысь}} бам $3", + "logentry-move-move": "$1 $3 бамлы $4 выль ним {{GENDER:$2|сётӥз}}", "logentry-newusers-create": "$1 нимо учётной запись {{GENDER:$2|кылдытэмын}} вал", + "logentry-upload-upload": "$1 {{GENDER:$2|понӥз}} $3", "searchsuggest-search": "Утчано {{SITENAME}}", "searchsuggest-containing": "кудъёсаз вань...", "api-error-autoblocked": "Тон IP-адрес заблокировать эрказ луи, малы ке шуоно со заблокировать пользователь кутыны луоз.", diff --git a/tests/common/TestsAutoLoader.php b/tests/common/TestsAutoLoader.php index 66df315e10..b67c9abfee 100644 --- a/tests/common/TestsAutoLoader.php +++ b/tests/common/TestsAutoLoader.php @@ -29,11 +29,13 @@ $wgAutoloadClasses += [ # tests/common 'TestSetup' => "$testDir/common/TestSetup.php", + # tests/integration + 'MWHttpRequestTestCase' => "$testDir/integration/includes/http/MWHttpRequestTestCase.php", + # tests/parser 'DbTestPreviewer' => "$testDir/parser/DbTestPreviewer.php", 'DbTestRecorder' => "$testDir/parser/DbTestRecorder.php", 'DjVuSupport' => "$testDir/parser/DjVuSupport.php", - 'TestRecorder' => "$testDir/parser/TestRecorder.php", 'MultiTestRecorder' => "$testDir/parser/MultiTestRecorder.php", 'ParserTestMockParser' => "$testDir/parser/ParserTestMockParser.php", 'ParserTestRunner' => "$testDir/parser/ParserTestRunner.php", diff --git a/tests/integration/includes/http/CurlHttpRequestTest.php b/tests/integration/includes/http/CurlHttpRequestTest.php new file mode 100644 index 0000000000..04f80f434f --- /dev/null +++ b/tests/integration/includes/http/CurlHttpRequestTest.php @@ -0,0 +1,5 @@ +oldHttpEngine = Http::$httpEngine; + Http::$httpEngine = static::$httpEngine; + + try { + $request = MWHttpRequest::factory( 'null:' ); + } catch ( DomainException $e ) { + $this->markTestSkipped( static::$httpEngine . ' engine not supported' ); + } + + if ( static::$httpEngine === 'php' ) { + $this->assertInstanceOf( PhpHttpRequest::class, $request ); + } else { + $this->assertInstanceOf( CurlHttpRequest::class, $request ); + } + } + + public function tearDown() { + parent::tearDown(); + Http::$httpEngine = $this->oldHttpEngine; + } + + // -------------------- + + public function testIsRedirect() { + $request = MWHttpRequest::factory( 'http://httpbin.org/get' ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertFalse( $request->isRedirect() ); + + $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/1' ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertTrue( $request->isRedirect() ); + } + + public function testgetFinalUrl() { + $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/3' ); + if ( !$request->canFollowRedirects() ) { + $this->markTestSkipped( 'cannot follow redirects' ); + } + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertNotSame( 'http://httpbin.org/get', $request->getFinalUrl() ); + + $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/3', [ 'followRedirects' + => true ] ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertSame( 'http://httpbin.org/get', $request->getFinalUrl() ); + $this->assertResponseFieldValue( 'url', 'http://httpbin.org/get', $request ); + + $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/3', [ 'followRedirects' + => true ] ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertSame( 'http://httpbin.org/get', $request->getFinalUrl() ); + $this->assertResponseFieldValue( 'url', 'http://httpbin.org/get', $request ); + + if ( static::$httpEngine === 'curl' ) { + $this->markTestIncomplete( 'maxRedirects seems to be ignored by CurlHttpRequest' ); + return; + } + + $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/3', [ 'followRedirects' + => true, 'maxRedirects' => 1 ] ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertNotSame( 'http://httpbin.org/get', $request->getFinalUrl() ); + } + + public function testSetCookie() { + $request = MWHttpRequest::factory( 'http://httpbin.org/cookies' ); + $request->setCookie( 'foo', 'bar' ); + $request->setCookie( 'foo2', 'bar2', [ 'domain' => 'example.com' ] ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertResponseFieldValue( 'cookies', [ 'foo' => 'bar' ], $request ); + } + + public function testSetCookieJar() { + $request = MWHttpRequest::factory( 'http://httpbin.org/cookies' ); + $cookieJar = new CookieJar(); + $cookieJar->setCookie( 'foo', 'bar', [ 'domain' => 'httpbin.org' ] ); + $cookieJar->setCookie( 'foo2', 'bar2', [ 'domain' => 'example.com' ] ); + $request->setCookieJar( $cookieJar ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertResponseFieldValue( 'cookies', [ 'foo' => 'bar' ], $request ); + + $request = MWHttpRequest::factory( 'http://httpbin.org/cookies/set?foo=bar' ); + $cookieJar = new CookieJar(); + $request->setCookieJar( $cookieJar ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertHasCookie( 'foo', 'bar', $request->getCookieJar() ); + + $this->markTestIncomplete( 'CookieJar does not handle deletion' ); + return; + + $request = MWHttpRequest::factory( 'http://httpbin.org/cookies/delete?foo' ); + $cookieJar = new CookieJar(); + $cookieJar->setCookie( 'foo', 'bar', [ 'domain' => 'httpbin.org' ] ); + $cookieJar->setCookie( 'foo2', 'bar2', [ 'domain' => 'httpbin.org' ] ); + $request->setCookieJar( $cookieJar ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertNotHasCookie( 'foo', $request->getCookieJar() ); + $this->assertHasCookie( 'foo2', 'bar2', $request->getCookieJar() ); + } + + public function testGetResponseHeaders() { + $request = MWHttpRequest::factory( 'http://httpbin.org/response-headers?Foo=bar' ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $headers = array_change_key_case( $request->getResponseHeaders(), CASE_LOWER ); + $this->assertArrayHasKey( 'foo', $headers ); + $this->assertSame( $request->getResponseHeader( 'Foo' ), 'bar' ); + } + + public function testSetHeader() { + $request = MWHttpRequest::factory( 'http://httpbin.org/headers' ); + $request->setHeader( 'Foo', 'bar' ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertResponseFieldValue( [ 'headers', 'Foo' ], 'bar', $request ); + } + + public function testGetStatus() { + $request = MWHttpRequest::factory( 'http://httpbin.org/status/418' ); + $status = $request->execute(); + $this->assertFalse( $status->isOK() ); + $this->assertSame( $request->getStatus(), 418 ); + } + + public function testSetUserAgent() { + $request = MWHttpRequest::factory( 'http://httpbin.org/user-agent' ); + $request->setUserAgent( 'foo' ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertResponseFieldValue( 'user-agent', 'foo', $request ); + } + + public function testSetData() { + $request = MWHttpRequest::factory( 'http://httpbin.org/post', [ 'method' => 'POST' ] ); + $request->setData( [ 'foo' => 'bar', 'foo2' => 'bar2' ] ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $this->assertResponseFieldValue( 'form', [ 'foo' => 'bar', 'foo2' => 'bar2' ], $request ); + } + + public function testSetCallback() { + if ( static::$httpEngine === 'php' ) { + $this->markTestIncomplete( 'PhpHttpRequest does not use setCallback()' ); + return; + } + + $request = MWHttpRequest::factory( 'http://httpbin.org/ip' ); + $data = ''; + $request->setCallback( function ( $fh, $content ) use ( &$data ) { + $data .= $content; + return strlen( $content ); + } ); + $status = $request->execute(); + $this->assertTrue( $status->isGood() ); + $data = json_decode( $data, true ); + $this->assertInternalType( 'array', $data ); + $this->assertArrayHasKey( 'origin', $data ); + } + + // -------------------- + + protected function assertResponseFieldValue( $key, $expectedValue, MWHttpRequest $response ) { + $this->assertSame( 200, $response->getStatus(), 'response status is not 200' ); + $data = json_decode( $response->getContent(), true ); + $this->assertInternalType( 'array', $data, 'response is not JSON' ); + $keyPath = ''; + foreach ( (array)$key as $keySegment ) { + $keyPath .= ( $keyPath ? '.' : '' ) . $keySegment; + $this->assertArrayHasKey( $keySegment, $data, $keyPath . ' not found' ); + $data = $data[$keySegment]; + } + $this->assertSame( $expectedValue, $data ); + } + + protected function assertHasCookie( $expectedName, $expectedValue, CookieJar $cookieJar ) { + $cookieJar = TestingAccessWrapper::newFromObject( $cookieJar ); + $cookies = array_change_key_case( $cookieJar->cookie, CASE_LOWER ); + $this->assertArrayHasKey( strtolower( $expectedName ), $cookies ); + $cookie = TestingAccessWrapper::newFromObject( + $cookies[strtolower( $expectedName )] ); + $this->assertSame( $expectedValue, $cookie->value ); + } + + protected function assertNotHasCookie( $name, CookieJar $cookieJar ) { + $cookieJar = TestingAccessWrapper::newFromObject( $cookieJar ); + $this->assertArrayNotHasKey( strtolower( $name ), + array_change_key_case( $cookieJar->cookie, CASE_LOWER ) ); + } +} + diff --git a/tests/integration/includes/http/PhpHttpRequestTest.php b/tests/integration/includes/http/PhpHttpRequestTest.php new file mode 100644 index 0000000000..d0222a5e76 --- /dev/null +++ b/tests/integration/includes/http/PhpHttpRequestTest.php @@ -0,0 +1,5 @@ +assertEquals( $expected, $ok, $msg ); - } - - public static function cookieDomains() { - return [ - [ false, "org" ], - [ false, ".org" ], - [ true, "wikipedia.org" ], - [ true, ".wikipedia.org" ], - [ false, "co.uk" ], - [ false, ".co.uk" ], - [ false, "gov.uk" ], - [ false, ".gov.uk" ], - [ true, "supermarket.uk" ], - [ false, "uk" ], - [ false, ".uk" ], - [ false, "127.0.0." ], - [ false, "127." ], - [ false, "127.0.0.1." ], - [ true, "127.0.0.1" ], - [ false, "333.0.0.1" ], - [ true, "example.com" ], - [ false, "example.com." ], - [ true, ".example.com" ], - - [ true, ".example.com", "www.example.com" ], - [ false, "example.com", "www.example.com" ], - [ true, "127.0.0.1", "127.0.0.1" ], - [ false, "127.0.0.1", "localhost" ], - ]; - } - - /** - * Test Http::isValidURI() - * @bug 27854 : Http::isValidURI is too lax - * @dataProvider provideURI - * @covers Http::isValidURI - */ - public function testIsValidUri( $expect, $URI, $message = '' ) { - $this->assertEquals( - $expect, - (bool)Http::isValidURI( $URI ), - $message - ); - } - - /** - * @covers Http::getProxy - */ - public function testGetProxy() { - $this->setMwGlobals( 'wgHTTPProxy', 'proxy.domain.tld' ); - $this->assertEquals( - 'proxy.domain.tld', - Http::getProxy() - ); - } - - /** - * Feeds URI to test a long regular expression in Http::isValidURI - */ - public static function provideURI() { - /** Format: 'boolean expectation', 'URI to test', 'Optional message' */ - return [ - [ false, '¿non sens before!! http://a', 'Allow anything before URI' ], - - # (http|https) - only two schemes allowed - [ true, 'http://www.example.org/' ], - [ true, 'https://www.example.org/' ], - [ true, 'http://www.example.org', 'URI without directory' ], - [ true, 'http://a', 'Short name' ], - [ true, 'http://étoile', 'Allow UTF-8 in hostname' ], # 'étoile' is french for 'star' - [ false, '\\host\directory', 'CIFS share' ], - [ false, 'gopher://host/dir', 'Reject gopher scheme' ], - [ false, 'telnet://host', 'Reject telnet scheme' ], - - # :\/\/ - double slashes - [ false, 'http//example.org', 'Reject missing colon in protocol' ], - [ false, 'http:/example.org', 'Reject missing slash in protocol' ], - [ false, 'http:example.org', 'Must have two slashes' ], - # Following fail since hostname can be made of anything - [ false, 'http:///example.org', 'Must have exactly two slashes, not three' ], - - # (\w+:{0,1}\w*@)? - optional user:pass - [ true, 'http://user@host', 'Username provided' ], - [ true, 'http://user:@host', 'Username provided, no password' ], - [ true, 'http://user:pass@host', 'Username and password provided' ], - - # (\S+) - host part is made of anything not whitespaces - // commented these out in order to remove @group Broken - // @todo are these valid tests? if so, fix Http::isValidURI so it can handle them - // [ false, 'http://!"èèè¿¿¿~~\'', 'hostname is made of any non whitespace' ], - // [ false, 'http://exam:ple.org/', 'hostname can not use colons!' ], - - # (:[0-9]+)? - port number - [ true, 'http://example.org:80/' ], - [ true, 'https://example.org:80/' ], - [ true, 'http://example.org:443/' ], - [ true, 'https://example.org:443/' ], - - # Part after the hostname is / or / with something else - [ true, 'http://example/#' ], - [ true, 'http://example/!' ], - [ true, 'http://example/:' ], - [ true, 'http://example/.' ], - [ true, 'http://example/?' ], - [ true, 'http://example/+' ], - [ true, 'http://example/=' ], - [ true, 'http://example/&' ], - [ true, 'http://example/%' ], - [ true, 'http://example/@' ], - [ true, 'http://example/-' ], - [ true, 'http://example//' ], - [ true, 'http://example/&' ], - - # Fragment - [ true, 'http://exam#ple.org', ], # This one is valid, really! - [ true, 'http://example.org:80#anchor' ], - [ true, 'http://example.org/?id#anchor' ], - [ true, 'http://example.org/?#anchor' ], - - [ false, 'http://a ¿non !!sens after', 'Allow anything after URI' ], - ]; - } - - /** - * Warning: - * - * These tests are for code that makes use of an artifact of how CURL - * handles header reporting on redirect pages, and will need to be - * rewritten when bug 29232 is taken care of (high-level handling of - * HTTP redirects). - */ - public function testRelativeRedirections() { - $h = MWHttpRequestTester::factory( 'http://oldsite/file.ext', [], __METHOD__ ); - - # Forge a Location header - $h->setRespHeaders( 'location', [ - 'http://newsite/file.ext', - '/newfile.ext', - ] - ); - # Verify we correctly fix the Location - $this->assertEquals( - 'http://newsite/newfile.ext', - $h->getFinalUrl(), - "Relative file path Location: interpreted as full URL" - ); - - $h->setRespHeaders( 'location', [ - 'https://oldsite/file.ext' - ] - ); - $this->assertEquals( - 'https://oldsite/file.ext', - $h->getFinalUrl(), - "Location to the HTTPS version of the site" - ); - - $h->setRespHeaders( 'location', [ - '/anotherfile.ext', - 'http://anotherfile/hoster.ext', - 'https://anotherfile/hoster.ext' - ] - ); - $this->assertEquals( - 'https://anotherfile/hoster.ext', - $h->getFinalUrl( "Relative file path Location: should keep the latest host and scheme!" ) - ); - } - - /** - * Constant values are from PHP 5.3.28 using cURL 7.24.0 - * @see https://secure.php.net/manual/en/curl.constants.php - * - * All constant values are present so that developers don’t need to remember - * to add them if added at a later date. The commented out constants were - * not found anywhere in the MediaWiki core code. - * - * Commented out constants that were not available in: - * HipHop VM 3.3.0 (rel) - * Compiler: heads/master-0-g08810d920dfff59e0774cf2d651f92f13a637175 - * Repo schema: 3214fc2c684a4520485f715ee45f33f2182324b1 - * Extension API: 20140829 - * - * Commented out constants that were removed in PHP 5.6.0 - * - * @covers CurlHttpRequest::execute - */ - public function provideCurlConstants() { - return [ - [ 'CURLAUTH_ANY' ], - [ 'CURLAUTH_ANYSAFE' ], - [ 'CURLAUTH_BASIC' ], - [ 'CURLAUTH_DIGEST' ], - [ 'CURLAUTH_GSSNEGOTIATE' ], - [ 'CURLAUTH_NTLM' ], - // [ 'CURLCLOSEPOLICY_CALLBACK' ], // removed in PHP 5.6.0 - // [ 'CURLCLOSEPOLICY_LEAST_RECENTLY_USED' ], // removed in PHP 5.6.0 - // [ 'CURLCLOSEPOLICY_LEAST_TRAFFIC' ], // removed in PHP 5.6.0 - // [ 'CURLCLOSEPOLICY_OLDEST' ], // removed in PHP 5.6.0 - // [ 'CURLCLOSEPOLICY_SLOWEST' ], // removed in PHP 5.6.0 - [ 'CURLE_ABORTED_BY_CALLBACK' ], - [ 'CURLE_BAD_CALLING_ORDER' ], - [ 'CURLE_BAD_CONTENT_ENCODING' ], - [ 'CURLE_BAD_FUNCTION_ARGUMENT' ], - [ 'CURLE_BAD_PASSWORD_ENTERED' ], - [ 'CURLE_COULDNT_CONNECT' ], - [ 'CURLE_COULDNT_RESOLVE_HOST' ], - [ 'CURLE_COULDNT_RESOLVE_PROXY' ], - [ 'CURLE_FAILED_INIT' ], - [ 'CURLE_FILESIZE_EXCEEDED' ], - [ 'CURLE_FILE_COULDNT_READ_FILE' ], - [ 'CURLE_FTP_ACCESS_DENIED' ], - [ 'CURLE_FTP_BAD_DOWNLOAD_RESUME' ], - [ 'CURLE_FTP_CANT_GET_HOST' ], - [ 'CURLE_FTP_CANT_RECONNECT' ], - [ 'CURLE_FTP_COULDNT_GET_SIZE' ], - [ 'CURLE_FTP_COULDNT_RETR_FILE' ], - [ 'CURLE_FTP_COULDNT_SET_ASCII' ], - [ 'CURLE_FTP_COULDNT_SET_BINARY' ], - [ 'CURLE_FTP_COULDNT_STOR_FILE' ], - [ 'CURLE_FTP_COULDNT_USE_REST' ], - [ 'CURLE_FTP_PORT_FAILED' ], - [ 'CURLE_FTP_QUOTE_ERROR' ], - [ 'CURLE_FTP_SSL_FAILED' ], - [ 'CURLE_FTP_USER_PASSWORD_INCORRECT' ], - [ 'CURLE_FTP_WEIRD_227_FORMAT' ], - [ 'CURLE_FTP_WEIRD_PASS_REPLY' ], - [ 'CURLE_FTP_WEIRD_PASV_REPLY' ], - [ 'CURLE_FTP_WEIRD_SERVER_REPLY' ], - [ 'CURLE_FTP_WEIRD_USER_REPLY' ], - [ 'CURLE_FTP_WRITE_ERROR' ], - [ 'CURLE_FUNCTION_NOT_FOUND' ], - [ 'CURLE_GOT_NOTHING' ], - [ 'CURLE_HTTP_NOT_FOUND' ], - [ 'CURLE_HTTP_PORT_FAILED' ], - [ 'CURLE_HTTP_POST_ERROR' ], - [ 'CURLE_HTTP_RANGE_ERROR' ], - [ 'CURLE_LDAP_CANNOT_BIND' ], - [ 'CURLE_LDAP_INVALID_URL' ], - [ 'CURLE_LDAP_SEARCH_FAILED' ], - [ 'CURLE_LIBRARY_NOT_FOUND' ], - [ 'CURLE_MALFORMAT_USER' ], - [ 'CURLE_OBSOLETE' ], - [ 'CURLE_OK' ], - [ 'CURLE_OPERATION_TIMEOUTED' ], - [ 'CURLE_OUT_OF_MEMORY' ], - [ 'CURLE_PARTIAL_FILE' ], - [ 'CURLE_READ_ERROR' ], - [ 'CURLE_RECV_ERROR' ], - [ 'CURLE_SEND_ERROR' ], - [ 'CURLE_SHARE_IN_USE' ], - // [ 'CURLE_SSH' ], // not present in HHVM 3.3.0-dev - [ 'CURLE_SSL_CACERT' ], - [ 'CURLE_SSL_CERTPROBLEM' ], - [ 'CURLE_SSL_CIPHER' ], - [ 'CURLE_SSL_CONNECT_ERROR' ], - [ 'CURLE_SSL_ENGINE_NOTFOUND' ], - [ 'CURLE_SSL_ENGINE_SETFAILED' ], - [ 'CURLE_SSL_PEER_CERTIFICATE' ], - [ 'CURLE_TELNET_OPTION_SYNTAX' ], - [ 'CURLE_TOO_MANY_REDIRECTS' ], - [ 'CURLE_UNKNOWN_TELNET_OPTION' ], - [ 'CURLE_UNSUPPORTED_PROTOCOL' ], - [ 'CURLE_URL_MALFORMAT' ], - [ 'CURLE_URL_MALFORMAT_USER' ], - [ 'CURLE_WRITE_ERROR' ], - [ 'CURLFTPAUTH_DEFAULT' ], - [ 'CURLFTPAUTH_SSL' ], - [ 'CURLFTPAUTH_TLS' ], - // [ 'CURLFTPMETHOD_MULTICWD' ], // not present in HHVM 3.3.0-dev - // [ 'CURLFTPMETHOD_NOCWD' ], // not present in HHVM 3.3.0-dev - // [ 'CURLFTPMETHOD_SINGLECWD' ], // not present in HHVM 3.3.0-dev - [ 'CURLFTPSSL_ALL' ], - [ 'CURLFTPSSL_CONTROL' ], - [ 'CURLFTPSSL_NONE' ], - [ 'CURLFTPSSL_TRY' ], - // [ 'CURLINFO_CERTINFO' ], // not present in HHVM 3.3.0-dev - [ 'CURLINFO_CONNECT_TIME' ], - [ 'CURLINFO_CONTENT_LENGTH_DOWNLOAD' ], - [ 'CURLINFO_CONTENT_LENGTH_UPLOAD' ], - [ 'CURLINFO_CONTENT_TYPE' ], - [ 'CURLINFO_EFFECTIVE_URL' ], - [ 'CURLINFO_FILETIME' ], - [ 'CURLINFO_HEADER_OUT' ], - [ 'CURLINFO_HEADER_SIZE' ], - [ 'CURLINFO_HTTP_CODE' ], - [ 'CURLINFO_NAMELOOKUP_TIME' ], - [ 'CURLINFO_PRETRANSFER_TIME' ], - [ 'CURLINFO_PRIVATE' ], - [ 'CURLINFO_REDIRECT_COUNT' ], - [ 'CURLINFO_REDIRECT_TIME' ], - // [ 'CURLINFO_REDIRECT_URL' ], // not present in HHVM 3.3.0-dev - [ 'CURLINFO_REQUEST_SIZE' ], - [ 'CURLINFO_SIZE_DOWNLOAD' ], - [ 'CURLINFO_SIZE_UPLOAD' ], - [ 'CURLINFO_SPEED_DOWNLOAD' ], - [ 'CURLINFO_SPEED_UPLOAD' ], - [ 'CURLINFO_SSL_VERIFYRESULT' ], - [ 'CURLINFO_STARTTRANSFER_TIME' ], - [ 'CURLINFO_TOTAL_TIME' ], - [ 'CURLMSG_DONE' ], - [ 'CURLM_BAD_EASY_HANDLE' ], - [ 'CURLM_BAD_HANDLE' ], - [ 'CURLM_CALL_MULTI_PERFORM' ], - [ 'CURLM_INTERNAL_ERROR' ], - [ 'CURLM_OK' ], - [ 'CURLM_OUT_OF_MEMORY' ], - [ 'CURLOPT_AUTOREFERER' ], - [ 'CURLOPT_BINARYTRANSFER' ], - [ 'CURLOPT_BUFFERSIZE' ], - [ 'CURLOPT_CAINFO' ], - [ 'CURLOPT_CAPATH' ], - // [ 'CURLOPT_CERTINFO' ], // not present in HHVM 3.3.0-dev - // [ 'CURLOPT_CLOSEPOLICY' ], // removed in PHP 5.6.0 - [ 'CURLOPT_CONNECTTIMEOUT' ], - [ 'CURLOPT_CONNECTTIMEOUT_MS' ], - [ 'CURLOPT_COOKIE' ], - [ 'CURLOPT_COOKIEFILE' ], - [ 'CURLOPT_COOKIEJAR' ], - [ 'CURLOPT_COOKIESESSION' ], - [ 'CURLOPT_CRLF' ], - [ 'CURLOPT_CUSTOMREQUEST' ], - [ 'CURLOPT_DNS_CACHE_TIMEOUT' ], - [ 'CURLOPT_DNS_USE_GLOBAL_CACHE' ], - [ 'CURLOPT_EGDSOCKET' ], - [ 'CURLOPT_ENCODING' ], - [ 'CURLOPT_FAILONERROR' ], - [ 'CURLOPT_FILE' ], - [ 'CURLOPT_FILETIME' ], - [ 'CURLOPT_FOLLOWLOCATION' ], - [ 'CURLOPT_FORBID_REUSE' ], - [ 'CURLOPT_FRESH_CONNECT' ], - [ 'CURLOPT_FTPAPPEND' ], - [ 'CURLOPT_FTPLISTONLY' ], - [ 'CURLOPT_FTPPORT' ], - [ 'CURLOPT_FTPSSLAUTH' ], - [ 'CURLOPT_FTP_CREATE_MISSING_DIRS' ], - // [ 'CURLOPT_FTP_FILEMETHOD' ], // not present in HHVM 3.3.0-dev - // [ 'CURLOPT_FTP_SKIP_PASV_IP' ], // not present in HHVM 3.3.0-dev - [ 'CURLOPT_FTP_SSL' ], - [ 'CURLOPT_FTP_USE_EPRT' ], - [ 'CURLOPT_FTP_USE_EPSV' ], - [ 'CURLOPT_HEADER' ], - [ 'CURLOPT_HEADERFUNCTION' ], - [ 'CURLOPT_HTTP200ALIASES' ], - [ 'CURLOPT_HTTPAUTH' ], - [ 'CURLOPT_HTTPGET' ], - [ 'CURLOPT_HTTPHEADER' ], - [ 'CURLOPT_HTTPPROXYTUNNEL' ], - [ 'CURLOPT_HTTP_VERSION' ], - [ 'CURLOPT_INFILE' ], - [ 'CURLOPT_INFILESIZE' ], - [ 'CURLOPT_INTERFACE' ], - [ 'CURLOPT_IPRESOLVE' ], - // [ 'CURLOPT_KEYPASSWD' ], // not present in HHVM 3.3.0-dev - [ 'CURLOPT_KRB4LEVEL' ], - [ 'CURLOPT_LOW_SPEED_LIMIT' ], - [ 'CURLOPT_LOW_SPEED_TIME' ], - [ 'CURLOPT_MAXCONNECTS' ], - [ 'CURLOPT_MAXREDIRS' ], - // [ 'CURLOPT_MAX_RECV_SPEED_LARGE' ], // not present in HHVM 3.3.0-dev - // [ 'CURLOPT_MAX_SEND_SPEED_LARGE' ], // not present in HHVM 3.3.0-dev - [ 'CURLOPT_NETRC' ], - [ 'CURLOPT_NOBODY' ], - [ 'CURLOPT_NOPROGRESS' ], - [ 'CURLOPT_NOSIGNAL' ], - [ 'CURLOPT_PORT' ], - [ 'CURLOPT_POST' ], - [ 'CURLOPT_POSTFIELDS' ], - [ 'CURLOPT_POSTQUOTE' ], - [ 'CURLOPT_POSTREDIR' ], - [ 'CURLOPT_PRIVATE' ], - [ 'CURLOPT_PROGRESSFUNCTION' ], - // [ 'CURLOPT_PROTOCOLS' ], // not present in HHVM 3.3.0-dev - [ 'CURLOPT_PROXY' ], - [ 'CURLOPT_PROXYAUTH' ], - [ 'CURLOPT_PROXYPORT' ], - [ 'CURLOPT_PROXYTYPE' ], - [ 'CURLOPT_PROXYUSERPWD' ], - [ 'CURLOPT_PUT' ], - [ 'CURLOPT_QUOTE' ], - [ 'CURLOPT_RANDOM_FILE' ], - [ 'CURLOPT_RANGE' ], - [ 'CURLOPT_READDATA' ], - [ 'CURLOPT_READFUNCTION' ], - // [ 'CURLOPT_REDIR_PROTOCOLS' ], // not present in HHVM 3.3.0-dev - [ 'CURLOPT_REFERER' ], - [ 'CURLOPT_RESUME_FROM' ], - [ 'CURLOPT_RETURNTRANSFER' ], - // [ 'CURLOPT_SSH_AUTH_TYPES' ], // not present in HHVM 3.3.0-dev - // [ 'CURLOPT_SSH_HOST_PUBLIC_KEY_MD5' ], // not present in HHVM 3.3.0-dev - // [ 'CURLOPT_SSH_PRIVATE_KEYFILE' ], // not present in HHVM 3.3.0-dev - // [ 'CURLOPT_SSH_PUBLIC_KEYFILE' ], // not present in HHVM 3.3.0-dev - [ 'CURLOPT_SSLCERT' ], - [ 'CURLOPT_SSLCERTPASSWD' ], - [ 'CURLOPT_SSLCERTTYPE' ], - [ 'CURLOPT_SSLENGINE' ], - [ 'CURLOPT_SSLENGINE_DEFAULT' ], - [ 'CURLOPT_SSLKEY' ], - [ 'CURLOPT_SSLKEYPASSWD' ], - [ 'CURLOPT_SSLKEYTYPE' ], - [ 'CURLOPT_SSLVERSION' ], - [ 'CURLOPT_SSL_CIPHER_LIST' ], - [ 'CURLOPT_SSL_VERIFYHOST' ], - [ 'CURLOPT_SSL_VERIFYPEER' ], - [ 'CURLOPT_STDERR' ], - [ 'CURLOPT_TCP_NODELAY' ], - [ 'CURLOPT_TIMECONDITION' ], - [ 'CURLOPT_TIMEOUT' ], - [ 'CURLOPT_TIMEOUT_MS' ], - [ 'CURLOPT_TIMEVALUE' ], - [ 'CURLOPT_TRANSFERTEXT' ], - [ 'CURLOPT_UNRESTRICTED_AUTH' ], - [ 'CURLOPT_UPLOAD' ], - [ 'CURLOPT_URL' ], - [ 'CURLOPT_USERAGENT' ], - [ 'CURLOPT_USERPWD' ], - [ 'CURLOPT_VERBOSE' ], - [ 'CURLOPT_WRITEFUNCTION' ], - [ 'CURLOPT_WRITEHEADER' ], - // [ 'CURLPROTO_ALL' ], // not present in HHVM 3.3.0-dev - // [ 'CURLPROTO_DICT' ], // not present in HHVM 3.3.0-dev - // [ 'CURLPROTO_FILE' ], // not present in HHVM 3.3.0-dev - // [ 'CURLPROTO_FTP' ], // not present in HHVM 3.3.0-dev - // [ 'CURLPROTO_FTPS' ], // not present in HHVM 3.3.0-dev - // [ 'CURLPROTO_HTTP' ], // not present in HHVM 3.3.0-dev - // [ 'CURLPROTO_HTTPS' ], // not present in HHVM 3.3.0-dev - // [ 'CURLPROTO_LDAP' ], // not present in HHVM 3.3.0-dev - // [ 'CURLPROTO_LDAPS' ], // not present in HHVM 3.3.0-dev - // [ 'CURLPROTO_SCP' ], // not present in HHVM 3.3.0-dev - // [ 'CURLPROTO_SFTP' ], // not present in HHVM 3.3.0-dev - // [ 'CURLPROTO_TELNET' ], // not present in HHVM 3.3.0-dev - // [ 'CURLPROTO_TFTP' ], // not present in HHVM 3.3.0-dev - [ 'CURLPROXY_HTTP' ], - // [ 'CURLPROXY_SOCKS4' ], // not present in HHVM 3.3.0-dev - [ 'CURLPROXY_SOCKS5' ], - // [ 'CURLSSH_AUTH_DEFAULT' ], // not present in HHVM 3.3.0-dev - // [ 'CURLSSH_AUTH_HOST' ], // not present in HHVM 3.3.0-dev - // [ 'CURLSSH_AUTH_KEYBOARD' ], // not present in HHVM 3.3.0-dev - // [ 'CURLSSH_AUTH_NONE' ], // not present in HHVM 3.3.0-dev - // [ 'CURLSSH_AUTH_PASSWORD' ], // not present in HHVM 3.3.0-dev - // [ 'CURLSSH_AUTH_PUBLICKEY' ], // not present in HHVM 3.3.0-dev - [ 'CURLVERSION_NOW' ], - [ 'CURL_HTTP_VERSION_1_0' ], - [ 'CURL_HTTP_VERSION_1_1' ], - [ 'CURL_HTTP_VERSION_NONE' ], - [ 'CURL_IPRESOLVE_V4' ], - [ 'CURL_IPRESOLVE_V6' ], - [ 'CURL_IPRESOLVE_WHATEVER' ], - [ 'CURL_NETRC_IGNORED' ], - [ 'CURL_NETRC_OPTIONAL' ], - [ 'CURL_NETRC_REQUIRED' ], - [ 'CURL_TIMECOND_IFMODSINCE' ], - [ 'CURL_TIMECOND_IFUNMODSINCE' ], - [ 'CURL_TIMECOND_LASTMOD' ], - [ 'CURL_VERSION_IPV6' ], - [ 'CURL_VERSION_KERBEROS4' ], - [ 'CURL_VERSION_LIBZ' ], - [ 'CURL_VERSION_SSL' ], - ]; - } - - /** - * Added this test based on an issue experienced with HHVM 3.3.0-dev - * where it did not define a cURL constant. - * - * @bug 70570 - * @dataProvider provideCurlConstants - */ - public function testCurlConstants( $value ) { - $this->assertTrue( defined( $value ), $value . ' not defined' ); - } -} - -/** - * Class to let us overwrite MWHttpRequest respHeaders variable - */ -class MWHttpRequestTester extends MWHttpRequest { - // function derived from the MWHttpRequest factory function but - // returns appropriate tester class here - public static function factory( $url, $options = null, $caller = __METHOD__ ) { - if ( !Http::$httpEngine ) { - Http::$httpEngine = function_exists( 'curl_init' ) ? 'curl' : 'php'; - } elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) { - throw new MWException( __METHOD__ . ': curl (http://php.net/curl) is not installed, but' . - 'Http::$httpEngine is set to "curl"' ); - } - - switch ( Http::$httpEngine ) { - case 'curl': - return new CurlHttpRequestTester( $url, $options, $caller ); - case 'php': - if ( !wfIniGetBool( 'allow_url_fopen' ) ) { - throw new MWException( __METHOD__ . - ': allow_url_fopen needs to be enabled for pure PHP HTTP requests to work. ' - . 'If possible, curl should be used instead. See http://php.net/curl.' ); - } - - return new PhpHttpRequestTester( $url, $options, $caller ); - default: - } - } -} - -class CurlHttpRequestTester extends CurlHttpRequest { - function setRespHeaders( $name, $value ) { - $this->respHeaders[$name] = $value; - } -} - -class PhpHttpRequestTester extends PhpHttpRequest { - function setRespHeaders( $name, $value ) { - $this->respHeaders[$name] = $value; - } -} diff --git a/tests/phpunit/includes/PrefixSearchTest.php b/tests/phpunit/includes/PrefixSearchTest.php index bc43709fde..c5a7e04e30 100644 --- a/tests/phpunit/includes/PrefixSearchTest.php +++ b/tests/phpunit/includes/PrefixSearchTest.php @@ -129,11 +129,11 @@ class PrefixSearchTest extends MediaWikiLangTestCase { 'results' => [ 'Special:ActiveUsers', 'Special:AllMessages', - 'Special:AllMyFiles', + 'Special:AllMyUploads', ], // Third result when testing offset 'offsetresult' => [ - 'Special:AllMyUploads', + 'Special:AllPages', ], ] ], [ [ @@ -146,7 +146,7 @@ class PrefixSearchTest extends MediaWikiLangTestCase { ], // Third result when testing offset 'offsetresult' => [ - 'Special:UncategorizedImages', + 'Special:UncategorizedPages', ], ] ], [ [ diff --git a/tests/phpunit/includes/http/HttpTest.php b/tests/phpunit/includes/http/HttpTest.php new file mode 100644 index 0000000000..7e98d1c069 --- /dev/null +++ b/tests/phpunit/includes/http/HttpTest.php @@ -0,0 +1,534 @@ +assertEquals( $expected, $ok, $msg ); + } + + public static function cookieDomains() { + return [ + [ false, "org" ], + [ false, ".org" ], + [ true, "wikipedia.org" ], + [ true, ".wikipedia.org" ], + [ false, "co.uk" ], + [ false, ".co.uk" ], + [ false, "gov.uk" ], + [ false, ".gov.uk" ], + [ true, "supermarket.uk" ], + [ false, "uk" ], + [ false, ".uk" ], + [ false, "127.0.0." ], + [ false, "127." ], + [ false, "127.0.0.1." ], + [ true, "127.0.0.1" ], + [ false, "333.0.0.1" ], + [ true, "example.com" ], + [ false, "example.com." ], + [ true, ".example.com" ], + + [ true, ".example.com", "www.example.com" ], + [ false, "example.com", "www.example.com" ], + [ true, "127.0.0.1", "127.0.0.1" ], + [ false, "127.0.0.1", "localhost" ], + ]; + } + + /** + * Test Http::isValidURI() + * @bug 27854 : Http::isValidURI is too lax + * @dataProvider provideURI + * @covers Http::isValidURI + */ + public function testIsValidUri( $expect, $URI, $message = '' ) { + $this->assertEquals( + $expect, + (bool)Http::isValidURI( $URI ), + $message + ); + } + + /** + * @covers Http::getProxy + */ + public function testGetProxy() { + $this->setMwGlobals( 'wgHTTPProxy', 'proxy.domain.tld' ); + $this->assertEquals( + 'proxy.domain.tld', + Http::getProxy() + ); + } + + /** + * Feeds URI to test a long regular expression in Http::isValidURI + */ + public static function provideURI() { + /** Format: 'boolean expectation', 'URI to test', 'Optional message' */ + return [ + [ false, '¿non sens before!! http://a', 'Allow anything before URI' ], + + # (http|https) - only two schemes allowed + [ true, 'http://www.example.org/' ], + [ true, 'https://www.example.org/' ], + [ true, 'http://www.example.org', 'URI without directory' ], + [ true, 'http://a', 'Short name' ], + [ true, 'http://étoile', 'Allow UTF-8 in hostname' ], # 'étoile' is french for 'star' + [ false, '\\host\directory', 'CIFS share' ], + [ false, 'gopher://host/dir', 'Reject gopher scheme' ], + [ false, 'telnet://host', 'Reject telnet scheme' ], + + # :\/\/ - double slashes + [ false, 'http//example.org', 'Reject missing colon in protocol' ], + [ false, 'http:/example.org', 'Reject missing slash in protocol' ], + [ false, 'http:example.org', 'Must have two slashes' ], + # Following fail since hostname can be made of anything + [ false, 'http:///example.org', 'Must have exactly two slashes, not three' ], + + # (\w+:{0,1}\w*@)? - optional user:pass + [ true, 'http://user@host', 'Username provided' ], + [ true, 'http://user:@host', 'Username provided, no password' ], + [ true, 'http://user:pass@host', 'Username and password provided' ], + + # (\S+) - host part is made of anything not whitespaces + // commented these out in order to remove @group Broken + // @todo are these valid tests? if so, fix Http::isValidURI so it can handle them + // [ false, 'http://!"èèè¿¿¿~~\'', 'hostname is made of any non whitespace' ], + // [ false, 'http://exam:ple.org/', 'hostname can not use colons!' ], + + # (:[0-9]+)? - port number + [ true, 'http://example.org:80/' ], + [ true, 'https://example.org:80/' ], + [ true, 'http://example.org:443/' ], + [ true, 'https://example.org:443/' ], + + # Part after the hostname is / or / with something else + [ true, 'http://example/#' ], + [ true, 'http://example/!' ], + [ true, 'http://example/:' ], + [ true, 'http://example/.' ], + [ true, 'http://example/?' ], + [ true, 'http://example/+' ], + [ true, 'http://example/=' ], + [ true, 'http://example/&' ], + [ true, 'http://example/%' ], + [ true, 'http://example/@' ], + [ true, 'http://example/-' ], + [ true, 'http://example//' ], + [ true, 'http://example/&' ], + + # Fragment + [ true, 'http://exam#ple.org', ], # This one is valid, really! + [ true, 'http://example.org:80#anchor' ], + [ true, 'http://example.org/?id#anchor' ], + [ true, 'http://example.org/?#anchor' ], + + [ false, 'http://a ¿non !!sens after', 'Allow anything after URI' ], + ]; + } + + /** + * Warning: + * + * These tests are for code that makes use of an artifact of how CURL + * handles header reporting on redirect pages, and will need to be + * rewritten when bug 29232 is taken care of (high-level handling of + * HTTP redirects). + */ + public function testRelativeRedirections() { + $h = MWHttpRequestTester::factory( 'http://oldsite/file.ext', [], __METHOD__ ); + + # Forge a Location header + $h->setRespHeaders( 'location', [ + 'http://newsite/file.ext', + '/newfile.ext', + ] + ); + # Verify we correctly fix the Location + $this->assertEquals( + 'http://newsite/newfile.ext', + $h->getFinalUrl(), + "Relative file path Location: interpreted as full URL" + ); + + $h->setRespHeaders( 'location', [ + 'https://oldsite/file.ext' + ] + ); + $this->assertEquals( + 'https://oldsite/file.ext', + $h->getFinalUrl(), + "Location to the HTTPS version of the site" + ); + + $h->setRespHeaders( 'location', [ + '/anotherfile.ext', + 'http://anotherfile/hoster.ext', + 'https://anotherfile/hoster.ext' + ] + ); + $this->assertEquals( + 'https://anotherfile/hoster.ext', + $h->getFinalUrl( "Relative file path Location: should keep the latest host and scheme!" ) + ); + } + + /** + * Constant values are from PHP 5.3.28 using cURL 7.24.0 + * @see https://secure.php.net/manual/en/curl.constants.php + * + * All constant values are present so that developers don’t need to remember + * to add them if added at a later date. The commented out constants were + * not found anywhere in the MediaWiki core code. + * + * Commented out constants that were not available in: + * HipHop VM 3.3.0 (rel) + * Compiler: heads/master-0-g08810d920dfff59e0774cf2d651f92f13a637175 + * Repo schema: 3214fc2c684a4520485f715ee45f33f2182324b1 + * Extension API: 20140829 + * + * Commented out constants that were removed in PHP 5.6.0 + * + * @covers CurlHttpRequest::execute + */ + public function provideCurlConstants() { + return [ + [ 'CURLAUTH_ANY' ], + [ 'CURLAUTH_ANYSAFE' ], + [ 'CURLAUTH_BASIC' ], + [ 'CURLAUTH_DIGEST' ], + [ 'CURLAUTH_GSSNEGOTIATE' ], + [ 'CURLAUTH_NTLM' ], + // [ 'CURLCLOSEPOLICY_CALLBACK' ], // removed in PHP 5.6.0 + // [ 'CURLCLOSEPOLICY_LEAST_RECENTLY_USED' ], // removed in PHP 5.6.0 + // [ 'CURLCLOSEPOLICY_LEAST_TRAFFIC' ], // removed in PHP 5.6.0 + // [ 'CURLCLOSEPOLICY_OLDEST' ], // removed in PHP 5.6.0 + // [ 'CURLCLOSEPOLICY_SLOWEST' ], // removed in PHP 5.6.0 + [ 'CURLE_ABORTED_BY_CALLBACK' ], + [ 'CURLE_BAD_CALLING_ORDER' ], + [ 'CURLE_BAD_CONTENT_ENCODING' ], + [ 'CURLE_BAD_FUNCTION_ARGUMENT' ], + [ 'CURLE_BAD_PASSWORD_ENTERED' ], + [ 'CURLE_COULDNT_CONNECT' ], + [ 'CURLE_COULDNT_RESOLVE_HOST' ], + [ 'CURLE_COULDNT_RESOLVE_PROXY' ], + [ 'CURLE_FAILED_INIT' ], + [ 'CURLE_FILESIZE_EXCEEDED' ], + [ 'CURLE_FILE_COULDNT_READ_FILE' ], + [ 'CURLE_FTP_ACCESS_DENIED' ], + [ 'CURLE_FTP_BAD_DOWNLOAD_RESUME' ], + [ 'CURLE_FTP_CANT_GET_HOST' ], + [ 'CURLE_FTP_CANT_RECONNECT' ], + [ 'CURLE_FTP_COULDNT_GET_SIZE' ], + [ 'CURLE_FTP_COULDNT_RETR_FILE' ], + [ 'CURLE_FTP_COULDNT_SET_ASCII' ], + [ 'CURLE_FTP_COULDNT_SET_BINARY' ], + [ 'CURLE_FTP_COULDNT_STOR_FILE' ], + [ 'CURLE_FTP_COULDNT_USE_REST' ], + [ 'CURLE_FTP_PORT_FAILED' ], + [ 'CURLE_FTP_QUOTE_ERROR' ], + [ 'CURLE_FTP_SSL_FAILED' ], + [ 'CURLE_FTP_USER_PASSWORD_INCORRECT' ], + [ 'CURLE_FTP_WEIRD_227_FORMAT' ], + [ 'CURLE_FTP_WEIRD_PASS_REPLY' ], + [ 'CURLE_FTP_WEIRD_PASV_REPLY' ], + [ 'CURLE_FTP_WEIRD_SERVER_REPLY' ], + [ 'CURLE_FTP_WEIRD_USER_REPLY' ], + [ 'CURLE_FTP_WRITE_ERROR' ], + [ 'CURLE_FUNCTION_NOT_FOUND' ], + [ 'CURLE_GOT_NOTHING' ], + [ 'CURLE_HTTP_NOT_FOUND' ], + [ 'CURLE_HTTP_PORT_FAILED' ], + [ 'CURLE_HTTP_POST_ERROR' ], + [ 'CURLE_HTTP_RANGE_ERROR' ], + [ 'CURLE_LDAP_CANNOT_BIND' ], + [ 'CURLE_LDAP_INVALID_URL' ], + [ 'CURLE_LDAP_SEARCH_FAILED' ], + [ 'CURLE_LIBRARY_NOT_FOUND' ], + [ 'CURLE_MALFORMAT_USER' ], + [ 'CURLE_OBSOLETE' ], + [ 'CURLE_OK' ], + [ 'CURLE_OPERATION_TIMEOUTED' ], + [ 'CURLE_OUT_OF_MEMORY' ], + [ 'CURLE_PARTIAL_FILE' ], + [ 'CURLE_READ_ERROR' ], + [ 'CURLE_RECV_ERROR' ], + [ 'CURLE_SEND_ERROR' ], + [ 'CURLE_SHARE_IN_USE' ], + // [ 'CURLE_SSH' ], // not present in HHVM 3.3.0-dev + [ 'CURLE_SSL_CACERT' ], + [ 'CURLE_SSL_CERTPROBLEM' ], + [ 'CURLE_SSL_CIPHER' ], + [ 'CURLE_SSL_CONNECT_ERROR' ], + [ 'CURLE_SSL_ENGINE_NOTFOUND' ], + [ 'CURLE_SSL_ENGINE_SETFAILED' ], + [ 'CURLE_SSL_PEER_CERTIFICATE' ], + [ 'CURLE_TELNET_OPTION_SYNTAX' ], + [ 'CURLE_TOO_MANY_REDIRECTS' ], + [ 'CURLE_UNKNOWN_TELNET_OPTION' ], + [ 'CURLE_UNSUPPORTED_PROTOCOL' ], + [ 'CURLE_URL_MALFORMAT' ], + [ 'CURLE_URL_MALFORMAT_USER' ], + [ 'CURLE_WRITE_ERROR' ], + [ 'CURLFTPAUTH_DEFAULT' ], + [ 'CURLFTPAUTH_SSL' ], + [ 'CURLFTPAUTH_TLS' ], + // [ 'CURLFTPMETHOD_MULTICWD' ], // not present in HHVM 3.3.0-dev + // [ 'CURLFTPMETHOD_NOCWD' ], // not present in HHVM 3.3.0-dev + // [ 'CURLFTPMETHOD_SINGLECWD' ], // not present in HHVM 3.3.0-dev + [ 'CURLFTPSSL_ALL' ], + [ 'CURLFTPSSL_CONTROL' ], + [ 'CURLFTPSSL_NONE' ], + [ 'CURLFTPSSL_TRY' ], + // [ 'CURLINFO_CERTINFO' ], // not present in HHVM 3.3.0-dev + [ 'CURLINFO_CONNECT_TIME' ], + [ 'CURLINFO_CONTENT_LENGTH_DOWNLOAD' ], + [ 'CURLINFO_CONTENT_LENGTH_UPLOAD' ], + [ 'CURLINFO_CONTENT_TYPE' ], + [ 'CURLINFO_EFFECTIVE_URL' ], + [ 'CURLINFO_FILETIME' ], + [ 'CURLINFO_HEADER_OUT' ], + [ 'CURLINFO_HEADER_SIZE' ], + [ 'CURLINFO_HTTP_CODE' ], + [ 'CURLINFO_NAMELOOKUP_TIME' ], + [ 'CURLINFO_PRETRANSFER_TIME' ], + [ 'CURLINFO_PRIVATE' ], + [ 'CURLINFO_REDIRECT_COUNT' ], + [ 'CURLINFO_REDIRECT_TIME' ], + // [ 'CURLINFO_REDIRECT_URL' ], // not present in HHVM 3.3.0-dev + [ 'CURLINFO_REQUEST_SIZE' ], + [ 'CURLINFO_SIZE_DOWNLOAD' ], + [ 'CURLINFO_SIZE_UPLOAD' ], + [ 'CURLINFO_SPEED_DOWNLOAD' ], + [ 'CURLINFO_SPEED_UPLOAD' ], + [ 'CURLINFO_SSL_VERIFYRESULT' ], + [ 'CURLINFO_STARTTRANSFER_TIME' ], + [ 'CURLINFO_TOTAL_TIME' ], + [ 'CURLMSG_DONE' ], + [ 'CURLM_BAD_EASY_HANDLE' ], + [ 'CURLM_BAD_HANDLE' ], + [ 'CURLM_CALL_MULTI_PERFORM' ], + [ 'CURLM_INTERNAL_ERROR' ], + [ 'CURLM_OK' ], + [ 'CURLM_OUT_OF_MEMORY' ], + [ 'CURLOPT_AUTOREFERER' ], + [ 'CURLOPT_BINARYTRANSFER' ], + [ 'CURLOPT_BUFFERSIZE' ], + [ 'CURLOPT_CAINFO' ], + [ 'CURLOPT_CAPATH' ], + // [ 'CURLOPT_CERTINFO' ], // not present in HHVM 3.3.0-dev + // [ 'CURLOPT_CLOSEPOLICY' ], // removed in PHP 5.6.0 + [ 'CURLOPT_CONNECTTIMEOUT' ], + [ 'CURLOPT_CONNECTTIMEOUT_MS' ], + [ 'CURLOPT_COOKIE' ], + [ 'CURLOPT_COOKIEFILE' ], + [ 'CURLOPT_COOKIEJAR' ], + [ 'CURLOPT_COOKIESESSION' ], + [ 'CURLOPT_CRLF' ], + [ 'CURLOPT_CUSTOMREQUEST' ], + [ 'CURLOPT_DNS_CACHE_TIMEOUT' ], + [ 'CURLOPT_DNS_USE_GLOBAL_CACHE' ], + [ 'CURLOPT_EGDSOCKET' ], + [ 'CURLOPT_ENCODING' ], + [ 'CURLOPT_FAILONERROR' ], + [ 'CURLOPT_FILE' ], + [ 'CURLOPT_FILETIME' ], + [ 'CURLOPT_FOLLOWLOCATION' ], + [ 'CURLOPT_FORBID_REUSE' ], + [ 'CURLOPT_FRESH_CONNECT' ], + [ 'CURLOPT_FTPAPPEND' ], + [ 'CURLOPT_FTPLISTONLY' ], + [ 'CURLOPT_FTPPORT' ], + [ 'CURLOPT_FTPSSLAUTH' ], + [ 'CURLOPT_FTP_CREATE_MISSING_DIRS' ], + // [ 'CURLOPT_FTP_FILEMETHOD' ], // not present in HHVM 3.3.0-dev + // [ 'CURLOPT_FTP_SKIP_PASV_IP' ], // not present in HHVM 3.3.0-dev + [ 'CURLOPT_FTP_SSL' ], + [ 'CURLOPT_FTP_USE_EPRT' ], + [ 'CURLOPT_FTP_USE_EPSV' ], + [ 'CURLOPT_HEADER' ], + [ 'CURLOPT_HEADERFUNCTION' ], + [ 'CURLOPT_HTTP200ALIASES' ], + [ 'CURLOPT_HTTPAUTH' ], + [ 'CURLOPT_HTTPGET' ], + [ 'CURLOPT_HTTPHEADER' ], + [ 'CURLOPT_HTTPPROXYTUNNEL' ], + [ 'CURLOPT_HTTP_VERSION' ], + [ 'CURLOPT_INFILE' ], + [ 'CURLOPT_INFILESIZE' ], + [ 'CURLOPT_INTERFACE' ], + [ 'CURLOPT_IPRESOLVE' ], + // [ 'CURLOPT_KEYPASSWD' ], // not present in HHVM 3.3.0-dev + [ 'CURLOPT_KRB4LEVEL' ], + [ 'CURLOPT_LOW_SPEED_LIMIT' ], + [ 'CURLOPT_LOW_SPEED_TIME' ], + [ 'CURLOPT_MAXCONNECTS' ], + [ 'CURLOPT_MAXREDIRS' ], + // [ 'CURLOPT_MAX_RECV_SPEED_LARGE' ], // not present in HHVM 3.3.0-dev + // [ 'CURLOPT_MAX_SEND_SPEED_LARGE' ], // not present in HHVM 3.3.0-dev + [ 'CURLOPT_NETRC' ], + [ 'CURLOPT_NOBODY' ], + [ 'CURLOPT_NOPROGRESS' ], + [ 'CURLOPT_NOSIGNAL' ], + [ 'CURLOPT_PORT' ], + [ 'CURLOPT_POST' ], + [ 'CURLOPT_POSTFIELDS' ], + [ 'CURLOPT_POSTQUOTE' ], + [ 'CURLOPT_POSTREDIR' ], + [ 'CURLOPT_PRIVATE' ], + [ 'CURLOPT_PROGRESSFUNCTION' ], + // [ 'CURLOPT_PROTOCOLS' ], // not present in HHVM 3.3.0-dev + [ 'CURLOPT_PROXY' ], + [ 'CURLOPT_PROXYAUTH' ], + [ 'CURLOPT_PROXYPORT' ], + [ 'CURLOPT_PROXYTYPE' ], + [ 'CURLOPT_PROXYUSERPWD' ], + [ 'CURLOPT_PUT' ], + [ 'CURLOPT_QUOTE' ], + [ 'CURLOPT_RANDOM_FILE' ], + [ 'CURLOPT_RANGE' ], + [ 'CURLOPT_READDATA' ], + [ 'CURLOPT_READFUNCTION' ], + // [ 'CURLOPT_REDIR_PROTOCOLS' ], // not present in HHVM 3.3.0-dev + [ 'CURLOPT_REFERER' ], + [ 'CURLOPT_RESUME_FROM' ], + [ 'CURLOPT_RETURNTRANSFER' ], + // [ 'CURLOPT_SSH_AUTH_TYPES' ], // not present in HHVM 3.3.0-dev + // [ 'CURLOPT_SSH_HOST_PUBLIC_KEY_MD5' ], // not present in HHVM 3.3.0-dev + // [ 'CURLOPT_SSH_PRIVATE_KEYFILE' ], // not present in HHVM 3.3.0-dev + // [ 'CURLOPT_SSH_PUBLIC_KEYFILE' ], // not present in HHVM 3.3.0-dev + [ 'CURLOPT_SSLCERT' ], + [ 'CURLOPT_SSLCERTPASSWD' ], + [ 'CURLOPT_SSLCERTTYPE' ], + [ 'CURLOPT_SSLENGINE' ], + [ 'CURLOPT_SSLENGINE_DEFAULT' ], + [ 'CURLOPT_SSLKEY' ], + [ 'CURLOPT_SSLKEYPASSWD' ], + [ 'CURLOPT_SSLKEYTYPE' ], + [ 'CURLOPT_SSLVERSION' ], + [ 'CURLOPT_SSL_CIPHER_LIST' ], + [ 'CURLOPT_SSL_VERIFYHOST' ], + [ 'CURLOPT_SSL_VERIFYPEER' ], + [ 'CURLOPT_STDERR' ], + [ 'CURLOPT_TCP_NODELAY' ], + [ 'CURLOPT_TIMECONDITION' ], + [ 'CURLOPT_TIMEOUT' ], + [ 'CURLOPT_TIMEOUT_MS' ], + [ 'CURLOPT_TIMEVALUE' ], + [ 'CURLOPT_TRANSFERTEXT' ], + [ 'CURLOPT_UNRESTRICTED_AUTH' ], + [ 'CURLOPT_UPLOAD' ], + [ 'CURLOPT_URL' ], + [ 'CURLOPT_USERAGENT' ], + [ 'CURLOPT_USERPWD' ], + [ 'CURLOPT_VERBOSE' ], + [ 'CURLOPT_WRITEFUNCTION' ], + [ 'CURLOPT_WRITEHEADER' ], + // [ 'CURLPROTO_ALL' ], // not present in HHVM 3.3.0-dev + // [ 'CURLPROTO_DICT' ], // not present in HHVM 3.3.0-dev + // [ 'CURLPROTO_FILE' ], // not present in HHVM 3.3.0-dev + // [ 'CURLPROTO_FTP' ], // not present in HHVM 3.3.0-dev + // [ 'CURLPROTO_FTPS' ], // not present in HHVM 3.3.0-dev + // [ 'CURLPROTO_HTTP' ], // not present in HHVM 3.3.0-dev + // [ 'CURLPROTO_HTTPS' ], // not present in HHVM 3.3.0-dev + // [ 'CURLPROTO_LDAP' ], // not present in HHVM 3.3.0-dev + // [ 'CURLPROTO_LDAPS' ], // not present in HHVM 3.3.0-dev + // [ 'CURLPROTO_SCP' ], // not present in HHVM 3.3.0-dev + // [ 'CURLPROTO_SFTP' ], // not present in HHVM 3.3.0-dev + // [ 'CURLPROTO_TELNET' ], // not present in HHVM 3.3.0-dev + // [ 'CURLPROTO_TFTP' ], // not present in HHVM 3.3.0-dev + [ 'CURLPROXY_HTTP' ], + // [ 'CURLPROXY_SOCKS4' ], // not present in HHVM 3.3.0-dev + [ 'CURLPROXY_SOCKS5' ], + // [ 'CURLSSH_AUTH_DEFAULT' ], // not present in HHVM 3.3.0-dev + // [ 'CURLSSH_AUTH_HOST' ], // not present in HHVM 3.3.0-dev + // [ 'CURLSSH_AUTH_KEYBOARD' ], // not present in HHVM 3.3.0-dev + // [ 'CURLSSH_AUTH_NONE' ], // not present in HHVM 3.3.0-dev + // [ 'CURLSSH_AUTH_PASSWORD' ], // not present in HHVM 3.3.0-dev + // [ 'CURLSSH_AUTH_PUBLICKEY' ], // not present in HHVM 3.3.0-dev + [ 'CURLVERSION_NOW' ], + [ 'CURL_HTTP_VERSION_1_0' ], + [ 'CURL_HTTP_VERSION_1_1' ], + [ 'CURL_HTTP_VERSION_NONE' ], + [ 'CURL_IPRESOLVE_V4' ], + [ 'CURL_IPRESOLVE_V6' ], + [ 'CURL_IPRESOLVE_WHATEVER' ], + [ 'CURL_NETRC_IGNORED' ], + [ 'CURL_NETRC_OPTIONAL' ], + [ 'CURL_NETRC_REQUIRED' ], + [ 'CURL_TIMECOND_IFMODSINCE' ], + [ 'CURL_TIMECOND_IFUNMODSINCE' ], + [ 'CURL_TIMECOND_LASTMOD' ], + [ 'CURL_VERSION_IPV6' ], + [ 'CURL_VERSION_KERBEROS4' ], + [ 'CURL_VERSION_LIBZ' ], + [ 'CURL_VERSION_SSL' ], + ]; + } + + /** + * Added this test based on an issue experienced with HHVM 3.3.0-dev + * where it did not define a cURL constant. + * + * @bug 70570 + * @dataProvider provideCurlConstants + */ + public function testCurlConstants( $value ) { + $this->assertTrue( defined( $value ), $value . ' not defined' ); + } +} + +/** + * Class to let us overwrite MWHttpRequest respHeaders variable + */ +class MWHttpRequestTester extends MWHttpRequest { + // function derived from the MWHttpRequest factory function but + // returns appropriate tester class here + public static function factory( $url, $options = null, $caller = __METHOD__ ) { + if ( !Http::$httpEngine ) { + Http::$httpEngine = function_exists( 'curl_init' ) ? 'curl' : 'php'; + } elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) { + throw new DomainException( __METHOD__ . ': curl (http://php.net/curl) is not installed, but' . + 'Http::$httpEngine is set to "curl"' ); + } + + switch ( Http::$httpEngine ) { + case 'curl': + return new CurlHttpRequestTester( $url, $options, $caller ); + case 'php': + if ( !wfIniGetBool( 'allow_url_fopen' ) ) { + throw new DomainException( __METHOD__ . + ': allow_url_fopen needs to be enabled for pure PHP HTTP requests to work. ' + . 'If possible, curl should be used instead. See http://php.net/curl.' ); + } + + return new PhpHttpRequestTester( $url, $options, $caller ); + default: + } + } +} + +class CurlHttpRequestTester extends CurlHttpRequest { + function setRespHeaders( $name, $value ) { + $this->respHeaders[$name] = $value; + } +} + +class PhpHttpRequestTester extends PhpHttpRequest { + function setRespHeaders( $name, $value ) { + $this->respHeaders[$name] = $value; + } +} diff --git a/tests/phpunit/includes/installer/DatabaseUpdaterTest.php b/tests/phpunit/includes/installer/DatabaseUpdaterTest.php deleted file mode 100644 index 2a75cf4020..0000000000 --- a/tests/phpunit/includes/installer/DatabaseUpdaterTest.php +++ /dev/null @@ -1,286 +0,0 @@ -setAppliedUpdates( "test", [] ); - $expected = "updatelist-test-" . time() . "0"; - $actual = $db->lastInsertData['ul_key']; - $this->assertEquals( $expected, $actual, var_export( $db->lastInsertData, true ) ); - $dbu->setAppliedUpdates( "test", [] ); - $expected = "updatelist-test-" . time() . "1"; - $actual = $db->lastInsertData['ul_key']; - $this->assertEquals( $expected, $actual, var_export( $db->lastInsertData, true ) ); - } -} - -class FakeDatabase extends Database { - public $lastInsertTable; - public $lastInsertData; - - function __construct() { - $this->cliMode = true; - $this->connLogger = new \Psr\Log\NullLogger(); - $this->queryLogger = new \Psr\Log\NullLogger(); - $this->errorLogger = function ( Exception $e ) { - wfWarn( get_class( $e ) . ": {$e->getMessage()}" ); - }; - $this->currentDomain = DatabaseDomain::newUnspecified(); - } - - function clearFlag( $arg, $remember = self::REMEMBER_NOTHING ) { - } - - function setFlag( $arg, $remember = self::REMEMBER_NOTHING ) { - } - - public function insert( $table, $a, $fname = __METHOD__, $options = [] ) { - $this->lastInsertTable = $table; - $this->lastInsertData = $a; - } - - /** - * Get the type of the DBMS, as it appears in $wgDBtype. - * - * @return string - */ - function getType() { - // TODO: Implement getType() method. - } - - /** - * Open a connection to the database. Usually aborts on failure - * - * @param string $server Database server host - * @param string $user Database user name - * @param string $password Database user password - * @param string $dbName Database name - * @return bool - * @throws DBConnectionError - */ - function open( $server, $user, $password, $dbName ) { - // TODO: Implement open() method. - } - - /** - * Fetch the next row from the given result object, in object form. - * Fields can be retrieved with $row->fieldname, with fields acting like - * member variables. - * If no more rows are available, false is returned. - * - * @param ResultWrapper|stdClass $res Object as returned from Database::query(), etc. - * @return stdClass|bool - * @throws DBUnexpectedError Thrown if the database returns an error - */ - function fetchObject( $res ) { - // TODO: Implement fetchObject() method. - } - - /** - * Fetch the next row from the given result object, in associative array - * form. Fields are retrieved with $row['fieldname']. - * If no more rows are available, false is returned. - * - * @param ResultWrapper $res Result object as returned from Database::query(), etc. - * @return array|bool - * @throws DBUnexpectedError Thrown if the database returns an error - */ - function fetchRow( $res ) { - // TODO: Implement fetchRow() method. - } - - /** - * Get the number of rows in a result object - * - * @param mixed $res A SQL result - * @return int - */ - function numRows( $res ) { - // TODO: Implement numRows() method. - } - - /** - * Get the number of fields in a result object - * @see https://secure.php.net/mysql_num_fields - * - * @param mixed $res A SQL result - * @return int - */ - function numFields( $res ) { - // TODO: Implement numFields() method. - } - - /** - * Get a field name in a result object - * @see https://secure.php.net/mysql_field_name - * - * @param mixed $res A SQL result - * @param int $n - * @return string - */ - function fieldName( $res, $n ) { - // TODO: Implement fieldName() method. - } - - /** - * Get the inserted value of an auto-increment row - * - * The value inserted should be fetched from nextSequenceValue() - * - * Example: - * $id = $dbw->nextSequenceValue( 'page_page_id_seq' ); - * $dbw->insert( 'page', [ 'page_id' => $id ] ); - * $id = $dbw->insertId(); - * - * @return int - */ - function insertId() { - // TODO: Implement insertId() method. - } - - /** - * Change the position of the cursor in a result object - * @see https://secure.php.net/mysql_data_seek - * - * @param mixed $res A SQL result - * @param int $row - */ - function dataSeek( $res, $row ) { - // TODO: Implement dataSeek() method. - } - - /** - * Get the last error number - * @see https://secure.php.net/mysql_errno - * - * @return int - */ - function lastErrno() { - // TODO: Implement lastErrno() method. - } - - /** - * Get a description of the last error - * @see https://secure.php.net/mysql_error - * - * @return string - */ - function lastError() { - // TODO: Implement lastError() method. - } - - /** - * mysql_fetch_field() wrapper - * Returns false if the field doesn't exist - * - * @param string $table Table name - * @param string $field Field name - * - * @return Field - */ - function fieldInfo( $table, $field ) { - // TODO: Implement fieldInfo() method. - } - - /** - * Get information about an index into an object - * @param string $table Table name - * @param string $index Index name - * @param string $fname Calling function name - * @return mixed Database-specific index description class or false if the index does not exist - */ - function indexInfo( $table, $index, $fname = __METHOD__ ) { - // TODO: Implement indexInfo() method. - } - - /** - * Get the number of rows affected by the last write query - * @see https://secure.php.net/mysql_affected_rows - * - * @return int - */ - function affectedRows() { - // TODO: Implement affectedRows() method. - } - - /** - * Wrapper for addslashes() - * - * @param string $s String to be slashed. - * @return string Slashed string. - */ - function strencode( $s ) { - // TODO: Implement strencode() method. - } - - /** - * Returns a wikitext link to the DB's website, e.g., - * return "[https://www.mysql.com/ MySQL]"; - * Should at least contain plain text, if for some reason - * your database has no website. - * - * @return string Wikitext of a link to the server software's web site - */ - function getSoftwareLink() { - // TODO: Implement getSoftwareLink() method. - } - - /** - * A string describing the current software version, like from - * mysql_get_server_info(). - * - * @return string Version information from the database server. - */ - function getServerVersion() { - // TODO: Implement getServerVersion() method. - } - - /** - * Closes underlying database connection - * @since 1.20 - * @return bool Whether connection was closed successfully - */ - protected function closeConnection() { - // TODO: Implement closeConnection() method. - } - - /** - * The DBMS-dependent part of query() - * - * @param string $sql SQL query. - * @return ResultWrapper|bool Result object to feed to fetchObject, - * fetchRow, ...; or false on failure - */ - protected function doQuery( $sql ) { - // TODO: Implement doQuery() method. - } -} - -class FakeDatabaseUpdater extends DatabaseUpdater { - function __construct( $db ) { - $this->db = $db; - self::$updateCounter = 0; - } - - /** - * Get an array of updates to perform on the database. Should return a - * multi-dimensional array. The main key is the MediaWiki version (1.12, - * 1.13...) with the values being arrays of updates, identical to how - * updaters.inc did it (for now) - * - * @return array - */ - protected function getCoreUpdateList() { - return []; - } - - public function canUseNewUpdatelog() { - return true; - } - - public function setAppliedUpdates( $version, $updates = [] ) { - parent::setAppliedUpdates( $version, $updates ); - } -} diff --git a/tests/phpunit/includes/libs/rdbms/connectionmanager/ConsistentReadConnectionManagerTest.php b/tests/phpunit/includes/libs/rdbms/connectionmanager/ConsistentReadConnectionManagerTest.php deleted file mode 100644 index 5ac97b2295..0000000000 --- a/tests/phpunit/includes/libs/rdbms/connectionmanager/ConsistentReadConnectionManagerTest.php +++ /dev/null @@ -1,164 +0,0 @@ -getMock( IDatabase::class ); - } - - /** - * @return LoadBalancer|PHPUnit_Framework_MockObject_MockObject - */ - private function getLoadBalancerMock() { - $lb = $this->getMockBuilder( LoadBalancer::class ) - ->disableOriginalConstructor() - ->getMock(); - - return $lb; - } - - public function testGetReadConnection() { - $database = $this->getIDatabaseMock(); - $lb = $this->getLoadBalancerMock(); - - $lb->expects( $this->once() ) - ->method( 'getConnection' ) - ->with( DB_REPLICA ) - ->will( $this->returnValue( $database ) ); - - $manager = new SessionConsistentConnectionManager( $lb ); - $actual = $manager->getReadConnection(); - - $this->assertSame( $database, $actual ); - } - - public function testGetReadConnectionReturnsWriteDbOnForceMatser() { - $database = $this->getIDatabaseMock(); - $lb = $this->getLoadBalancerMock(); - - $lb->expects( $this->once() ) - ->method( 'getConnection' ) - ->with( DB_MASTER ) - ->will( $this->returnValue( $database ) ); - - $manager = new SessionConsistentConnectionManager( $lb ); - $manager->prepareForUpdates(); - $actual = $manager->getReadConnection(); - - $this->assertSame( $database, $actual ); - } - - public function testGetWriteConnection() { - $database = $this->getIDatabaseMock(); - $lb = $this->getLoadBalancerMock(); - - $lb->expects( $this->once() ) - ->method( 'getConnection' ) - ->with( DB_MASTER ) - ->will( $this->returnValue( $database ) ); - - $manager = new SessionConsistentConnectionManager( $lb ); - $actual = $manager->getWriteConnection(); - - $this->assertSame( $database, $actual ); - } - - public function testForceMaster() { - $database = $this->getIDatabaseMock(); - $lb = $this->getLoadBalancerMock(); - - $lb->expects( $this->once() ) - ->method( 'getConnection' ) - ->with( DB_MASTER ) - ->will( $this->returnValue( $database ) ); - - $manager = new SessionConsistentConnectionManager( $lb ); - $manager->prepareForUpdates(); - $manager->getReadConnection(); - } - - public function testReleaseConnection() { - $database = $this->getIDatabaseMock(); - $lb = $this->getLoadBalancerMock(); - - $lb->expects( $this->once() ) - ->method( 'reuseConnection' ) - ->with( $database ) - ->will( $this->returnValue( null ) ); - - $manager = new SessionConsistentConnectionManager( $lb ); - $manager->releaseConnection( $database ); - } - - public function testBeginAtomicSection() { - $database = $this->getIDatabaseMock(); - $lb = $this->getLoadBalancerMock(); - - $lb->expects( $this->exactly( 2 ) ) - ->method( 'getConnection' ) - ->with( DB_MASTER ) - ->will( $this->returnValue( $database ) ); - - $database->expects( $this->once() ) - ->method( 'startAtomic' ) - ->will( $this->returnValue( null ) ); - - $manager = new SessionConsistentConnectionManager( $lb ); - $manager->beginAtomicSection( 'TEST' ); - - // Should also ask for a DB_MASTER connection. - // This is asserted by the $lb mock. - $manager->getReadConnection(); - } - - public function testCommitAtomicSection() { - $database = $this->getIDatabaseMock(); - $lb = $this->getLoadBalancerMock(); - - $lb->expects( $this->once() ) - ->method( 'reuseConnection' ) - ->with( $database ) - ->will( $this->returnValue( null ) ); - - $database->expects( $this->once() ) - ->method( 'endAtomic' ) - ->will( $this->returnValue( null ) ); - - $manager = new SessionConsistentConnectionManager( $lb ); - $manager->commitAtomicSection( $database, 'TEST' ); - } - - public function testRollbackAtomicSection() { - $database = $this->getIDatabaseMock(); - $lb = $this->getLoadBalancerMock(); - - $lb->expects( $this->once() ) - ->method( 'reuseConnection' ) - ->with( $database ) - ->will( $this->returnValue( null ) ); - - $database->expects( $this->once() ) - ->method( 'rollback' ) - ->will( $this->returnValue( null ) ); - - $manager = new SessionConsistentConnectionManager( $lb ); - $manager->rollbackAtomicSection( $database, 'TEST' ); - } - -} diff --git a/tests/phpunit/includes/libs/rdbms/connectionmanager/SessionConsistentConnectionManagerTest.php b/tests/phpunit/includes/libs/rdbms/connectionmanager/SessionConsistentConnectionManagerTest.php new file mode 100644 index 0000000000..4dcab82df8 --- /dev/null +++ b/tests/phpunit/includes/libs/rdbms/connectionmanager/SessionConsistentConnectionManagerTest.php @@ -0,0 +1,164 @@ +getMock( IDatabase::class ); + } + + /** + * @return LoadBalancer|PHPUnit_Framework_MockObject_MockObject + */ + private function getLoadBalancerMock() { + $lb = $this->getMockBuilder( LoadBalancer::class ) + ->disableOriginalConstructor() + ->getMock(); + + return $lb; + } + + public function testGetReadConnection() { + $database = $this->getIDatabaseMock(); + $lb = $this->getLoadBalancerMock(); + + $lb->expects( $this->once() ) + ->method( 'getConnection' ) + ->with( DB_REPLICA ) + ->will( $this->returnValue( $database ) ); + + $manager = new SessionConsistentConnectionManager( $lb ); + $actual = $manager->getReadConnection(); + + $this->assertSame( $database, $actual ); + } + + public function testGetReadConnectionReturnsWriteDbOnForceMatser() { + $database = $this->getIDatabaseMock(); + $lb = $this->getLoadBalancerMock(); + + $lb->expects( $this->once() ) + ->method( 'getConnection' ) + ->with( DB_MASTER ) + ->will( $this->returnValue( $database ) ); + + $manager = new SessionConsistentConnectionManager( $lb ); + $manager->prepareForUpdates(); + $actual = $manager->getReadConnection(); + + $this->assertSame( $database, $actual ); + } + + public function testGetWriteConnection() { + $database = $this->getIDatabaseMock(); + $lb = $this->getLoadBalancerMock(); + + $lb->expects( $this->once() ) + ->method( 'getConnection' ) + ->with( DB_MASTER ) + ->will( $this->returnValue( $database ) ); + + $manager = new SessionConsistentConnectionManager( $lb ); + $actual = $manager->getWriteConnection(); + + $this->assertSame( $database, $actual ); + } + + public function testForceMaster() { + $database = $this->getIDatabaseMock(); + $lb = $this->getLoadBalancerMock(); + + $lb->expects( $this->once() ) + ->method( 'getConnection' ) + ->with( DB_MASTER ) + ->will( $this->returnValue( $database ) ); + + $manager = new SessionConsistentConnectionManager( $lb ); + $manager->prepareForUpdates(); + $manager->getReadConnection(); + } + + public function testReleaseConnection() { + $database = $this->getIDatabaseMock(); + $lb = $this->getLoadBalancerMock(); + + $lb->expects( $this->once() ) + ->method( 'reuseConnection' ) + ->with( $database ) + ->will( $this->returnValue( null ) ); + + $manager = new SessionConsistentConnectionManager( $lb ); + $manager->releaseConnection( $database ); + } + + public function testBeginAtomicSection() { + $database = $this->getIDatabaseMock(); + $lb = $this->getLoadBalancerMock(); + + $lb->expects( $this->exactly( 2 ) ) + ->method( 'getConnection' ) + ->with( DB_MASTER ) + ->will( $this->returnValue( $database ) ); + + $database->expects( $this->once() ) + ->method( 'startAtomic' ) + ->will( $this->returnValue( null ) ); + + $manager = new SessionConsistentConnectionManager( $lb ); + $manager->beginAtomicSection( 'TEST' ); + + // Should also ask for a DB_MASTER connection. + // This is asserted by the $lb mock. + $manager->getReadConnection(); + } + + public function testCommitAtomicSection() { + $database = $this->getIDatabaseMock(); + $lb = $this->getLoadBalancerMock(); + + $lb->expects( $this->once() ) + ->method( 'reuseConnection' ) + ->with( $database ) + ->will( $this->returnValue( null ) ); + + $database->expects( $this->once() ) + ->method( 'endAtomic' ) + ->will( $this->returnValue( null ) ); + + $manager = new SessionConsistentConnectionManager( $lb ); + $manager->commitAtomicSection( $database, 'TEST' ); + } + + public function testRollbackAtomicSection() { + $database = $this->getIDatabaseMock(); + $lb = $this->getLoadBalancerMock(); + + $lb->expects( $this->once() ) + ->method( 'reuseConnection' ) + ->with( $database ) + ->will( $this->returnValue( null ) ); + + $database->expects( $this->once() ) + ->method( 'rollback' ) + ->will( $this->returnValue( null ) ); + + $manager = new SessionConsistentConnectionManager( $lb ); + $manager->rollbackAtomicSection( $database, 'TEST' ); + } + +} diff --git a/tests/phpunit/includes/registration/ExtensionRegistryTest.php b/tests/phpunit/includes/registration/ExtensionRegistryTest.php index 1de42650fd..9b57e1c3d1 100644 --- a/tests/phpunit/includes/registration/ExtensionRegistryTest.php +++ b/tests/phpunit/includes/registration/ExtensionRegistryTest.php @@ -259,7 +259,7 @@ class ExtensionRegistryTest extends MediaWikiTestCase { 'JsonZeroConfig' => [ 'namespace' => 480, 'nsName' => 'Zero', - 'isLocal' => false, + 'isLocal' => true, ], ], ], diff --git a/tests/phpunit/includes/search/SearchEnginePrefixTest.php b/tests/phpunit/includes/search/SearchEnginePrefixTest.php index e0de58866f..a88264bb78 100644 --- a/tests/phpunit/includes/search/SearchEnginePrefixTest.php +++ b/tests/phpunit/includes/search/SearchEnginePrefixTest.php @@ -126,11 +126,11 @@ class SearchEnginePrefixTest extends MediaWikiLangTestCase { 'results' => [ 'Special:ActiveUsers', 'Special:AllMessages', - 'Special:AllMyFiles', + 'Special:AllMyUploads', ], // Third result when testing offset 'offsetresult' => [ - 'Special:AllMyUploads', + 'Special:AllPages', ], ] ], [ [ @@ -143,7 +143,7 @@ class SearchEnginePrefixTest extends MediaWikiLangTestCase { ], // Third result when testing offset 'offsetresult' => [ - 'Special:UncategorizedImages', + 'Special:UncategorizedPages', ], ] ], [ [