Merge "[ExternalStore] Refactored external store classes to use a base class."
[lhc/web/wiklou.git] / tests / phpunit / includes / TitleTest.php
1 <?php
2
3 /**
4 *
5 * @group Database
6 * ^--- needed for language cache stuff
7 */
8 class TitleTest extends MediaWikiTestCase {
9
10 protected function setUp() {
11 parent::setUp();
12
13 $this->setMwGlobals( array(
14 'wgLanguageCode' => 'en',
15 'wgContLang' => Language::factory( 'en' ),
16 // User language
17 'wgLang' => Language::factory( 'en' ),
18 'wgAllowUserJs' => false,
19 'wgDefaultLanguageVariant' => false,
20 ) );
21 }
22
23 function testLegalChars() {
24 $titlechars = Title::legalChars();
25
26 foreach ( range( 1, 255 ) as $num ) {
27 $chr = chr( $num );
28 if ( strpos( "#[]{}<>|", $chr ) !== false || preg_match( "/[\\x00-\\x1f\\x7f]/", $chr ) ) {
29 $this->assertFalse( (bool)preg_match( "/[$titlechars]/", $chr ), "chr($num) = $chr is not a valid titlechar" );
30 } else {
31 $this->assertTrue( (bool)preg_match( "/[$titlechars]/", $chr ), "chr($num) = $chr is a valid titlechar" );
32 }
33 }
34 }
35
36 /**
37 * @dataProvider provideBug31100
38 */
39 function testBug31100FixSpecialName( $text, $expectedParam ) {
40 $title = Title::newFromText( $text );
41 $fixed = $title->fixSpecialName();
42 $stuff = explode( '/', $fixed->getDbKey(), 2 );
43 if ( count( $stuff ) == 2 ) {
44 $par = $stuff[1];
45 } else {
46 $par = null;
47 }
48 $this->assertEquals( $expectedParam, $par, "Bug 31100 regression check: Title->fixSpecialName() should preserve parameter" );
49 }
50
51 public static function provideBug31100() {
52 return array(
53 array( 'Special:Version', null ),
54 array( 'Special:Version/', '' ),
55 array( 'Special:Version/param', 'param' ),
56 );
57 }
58
59 /**
60 * Auth-less test of Title::isValidMoveOperation
61 *
62 * @group Database
63 * @param string $source
64 * @param string $target
65 * @param array|string|true $expected Required error
66 * @dataProvider provideTestIsValidMoveOperation
67 */
68 function testIsValidMoveOperation( $source, $target, $expected ) {
69 $title = Title::newFromText( $source );
70 $nt = Title::newFromText( $target );
71 $errors = $title->isValidMoveOperation( $nt, false );
72 if ( $expected === true ) {
73 $this->assertTrue( $errors );
74 } else {
75 $errors = $this->flattenErrorsArray( $errors );
76 foreach ( (array)$expected as $error ) {
77 $this->assertContains( $error, $errors );
78 }
79 }
80 }
81
82 /**
83 * Provides test parameter values for testIsValidMoveOperation()
84 */
85 function dataTestIsValidMoveOperation() {
86 return array(
87 array( 'Test', 'Test', 'selfmove' ),
88 array( 'File:Test.jpg', 'Page', 'imagenocrossnamespace' )
89 );
90 }
91
92 /**
93 * Auth-less test of Title::userCan
94 *
95 * @param array $whitelistRegexp
96 * @param string $source
97 * @param string $action
98 * @param array|string|true $expected Required error
99 *
100 * @covers Title::checkReadPermission
101 * @dataProvider dataWgWhitelistReadRegexp
102 */
103 function testWgWhitelistReadRegexp($whitelistRegexp, $source, $action, $expected) {
104
105 // $wgWhitelistReadRegexp must be an array. Since the provided test cases
106 // usually have only one regex, it is more concise to write the lonely regex
107 // as a string. Thus we cast to an array() to honor $wgWhitelistReadRegexp
108 // type requisite.
109 if( is_string( $whitelistRegexp ) ) {
110 $whitelistRegexp = array( $whitelistRegexp );
111 }
112
113 $title = Title::newFromDBkey( $source );
114
115 global $wgGroupPermissions;
116 $oldPermissions = $wgGroupPermissions;
117 // Disallow all so we can ensure our regex works
118 $wgGroupPermissions = array();
119 $wgGroupPermissions['*']['read'] = false;
120
121 global $wgWhitelistRead;
122 $oldWhitelist = $wgWhitelistRead;
123 // Undo any LocalSettings explicite whitelists so they won't cause a
124 // failing test to succeed. Set it to some random non sense just
125 // to make sure we properly test Title::checkReadPermissions()
126 $wgWhitelistRead = array( 'some random non sense title' );
127
128 global $wgWhitelistReadRegexp;
129 $oldWhitelistRegexp = $wgWhitelistReadRegexp;
130 $wgWhitelistReadRegexp = $whitelistRegexp ;
131
132 // Just use $wgUser which in test is a user object for '127.0.0.1'
133 global $wgUser;
134 // Invalidate user rights cache to take in account $wgGroupPermissions
135 // change above.
136 $wgUser->clearInstanceCache();
137 $errors = $title->userCan( $action, $wgUser );
138
139 // Restore globals
140 $wgGroupPermissions = $oldPermissions;
141 $wgWhitelistRead = $oldWhitelist;
142 $wgWhitelistReadRegexp = $oldWhitelistRegexp;
143
144 if( is_bool( $expected ) ) {
145 # Forge the assertion message depending on the assertion expectation
146 $allowableness = $expected
147 ? " should be allowed"
148 : " should NOT be allowed"
149 ;
150 $this->assertEquals( $expected, $errors, "User action '$action' on [[$source]] $allowableness." );
151 } else {
152 $errors = $this->flattenErrorsArray( $errors );
153 foreach ( (array)$expected as $error ) {
154 $this->assertContains( $error, $errors );
155 }
156 }
157 }
158
159 /**
160 * Provides test parameter values for testWgWhitelistReadRegexp()
161 */
162 function dataWgWhitelistReadRegexp() {
163 $ALLOWED = true;
164 $DISALLOWED = false;
165
166 return array(
167 // Everything, if this doesn't work, we're really in trouble
168 array( '/.*/', 'Main_Page', 'read', $ALLOWED ),
169 array( '/.*/', 'Main_Page', 'edit', $DISALLOWED ),
170
171 // We validate against the title name, not the db key
172 array( '/^Main_Page$/', 'Main_Page', 'read', $DISALLOWED ),
173 // Main page
174 array( '/^Main/', 'Main_Page', 'read', $ALLOWED ),
175 array( '/^Main.*/', 'Main_Page', 'read', $ALLOWED ),
176 // With spaces
177 array( '/Mic\sCheck/', 'Mic Check', 'read', $ALLOWED ),
178 // Unicode multibyte
179 // ...without unicode modifier
180 array( '/Unicode Test . Yes/', 'Unicode Test Ñ Yes', 'read', $DISALLOWED ),
181 // ...with unicode modifier
182 array( '/Unicode Test . Yes/u', 'Unicode Test Ñ Yes', 'read', $ALLOWED ),
183 // Case insensitive
184 array( '/MiC ChEcK/', 'mic check', 'read', $DISALLOWED ),
185 array( '/MiC ChEcK/i', 'mic check', 'read', $ALLOWED ),
186
187 // From DefaultSettings.php:
188 array( "@^UsEr.*@i", 'User is banned', 'read', $ALLOWED ),
189 array( "@^UsEr.*@i", 'User:John Doe', 'read', $ALLOWED ),
190
191 // With namespaces:
192 array( '/^Special:NewPages$/', 'Special:NewPages', 'read', $ALLOWED ),
193 array( null, 'Special:Newpages', 'read', $DISALLOWED ),
194
195 );
196 }
197
198 function flattenErrorsArray( $errors ) {
199 $result = array();
200 foreach ( $errors as $error ) {
201 $result[] = $error[0];
202 }
203 return $result;
204 }
205
206 public static function provideTestIsValidMoveOperation() {
207 return array(
208 array( 'Test', 'Test', 'selfmove' ),
209 array( 'File:Test.jpg', 'Page', 'imagenocrossnamespace' )
210 );
211 }
212
213 /**
214 * @dataProvider provideCasesForGetpageviewlanguage
215 */
216 function testGetpageviewlanguage( $expected, $titleText, $contLang, $lang, $variant, $msg = '' ) {
217 global $wgLanguageCode, $wgContLang, $wgLang, $wgDefaultLanguageVariant, $wgAllowUserJs;
218
219 // Setup environnement for this test
220 $wgLanguageCode = $contLang;
221 $wgContLang = Language::factory( $contLang );
222 $wgLang = Language::factory( $lang );
223 $wgDefaultLanguageVariant = $variant;
224 $wgAllowUserJs = true;
225
226 $title = Title::newFromText( $titleText );
227 $this->assertInstanceOf( 'Title', $title,
228 "Test must be passed a valid title text, you gave '$titleText'"
229 );
230 $this->assertEquals( $expected,
231 $title->getPageViewLanguage()->getCode(),
232 $msg
233 );
234 }
235
236 function provideCasesForGetpageviewlanguage() {
237 # Format:
238 # - expected
239 # - Title name
240 # - wgContLang (expected in most case)
241 # - wgLang (on some specific pages)
242 # - wgDefaultLanguageVariant
243 # - Optional message
244 return array(
245 array( 'fr', 'Help:I_need_somebody', 'fr', 'fr', false ),
246 array( 'es', 'Help:I_need_somebody', 'es', 'zh-tw', false ),
247 array( 'zh', 'Help:I_need_somebody', 'zh', 'zh-tw', false ),
248
249 array( 'es', 'Help:I_need_somebody', 'es', 'zh-tw', 'zh-cn' ),
250 array( 'es', 'MediaWiki:About', 'es', 'zh-tw', 'zh-cn' ),
251 array( 'es', 'MediaWiki:About/', 'es', 'zh-tw', 'zh-cn' ),
252 array( 'de', 'MediaWiki:About/de', 'es', 'zh-tw', 'zh-cn' ),
253 array( 'en', 'MediaWiki:Common.js', 'es', 'zh-tw', 'zh-cn' ),
254 array( 'en', 'MediaWiki:Common.css', 'es', 'zh-tw', 'zh-cn' ),
255 array( 'en', 'User:JohnDoe/Common.js', 'es', 'zh-tw', 'zh-cn' ),
256 array( 'en', 'User:JohnDoe/Monobook.css', 'es', 'zh-tw', 'zh-cn' ),
257
258 array( 'zh-cn', 'Help:I_need_somebody', 'zh', 'zh-tw', 'zh-cn' ),
259 array( 'zh', 'MediaWiki:About', 'zh', 'zh-tw', 'zh-cn' ),
260 array( 'zh', 'MediaWiki:About/', 'zh', 'zh-tw', 'zh-cn' ),
261 array( 'de', 'MediaWiki:About/de', 'zh', 'zh-tw', 'zh-cn' ),
262 array( 'zh-cn', 'MediaWiki:About/zh-cn', 'zh', 'zh-tw', 'zh-cn' ),
263 array( 'zh-tw', 'MediaWiki:About/zh-tw', 'zh', 'zh-tw', 'zh-cn' ),
264 array( 'en', 'MediaWiki:Common.js', 'zh', 'zh-tw', 'zh-cn' ),
265 array( 'en', 'MediaWiki:Common.css', 'zh', 'zh-tw', 'zh-cn' ),
266 array( 'en', 'User:JohnDoe/Common.js', 'zh', 'zh-tw', 'zh-cn' ),
267 array( 'en', 'User:JohnDoe/Monobook.css', 'zh', 'zh-tw', 'zh-cn' ),
268
269 array( 'zh-tw', 'Special:NewPages', 'es', 'zh-tw', 'zh-cn' ),
270 array( 'zh-tw', 'Special:NewPages', 'zh', 'zh-tw', 'zh-cn' ),
271
272 );
273 }
274
275 /**
276 * @dataProvider provideBaseTitleCases
277 */
278 function testExtractingBaseTextFromTitle( $title, $expected, $msg='' ) {
279 $title = Title::newFromText( $title );
280 $this->assertEquals( $expected,
281 $title->getBaseText(),
282 $msg
283 );
284 }
285
286 function provideBaseTitleCases() {
287 return array(
288 # Title, expected base, optional message
289 array('User:John_Doe/subOne/subTwo', 'John Doe/subOne' ),
290 array('User:Foo/Bar/Baz', 'Foo/Bar' ),
291 );
292 }
293
294 /**
295 * @dataProvider provideRootTitleCases
296 */
297 function testExtractingRootTextFromTitle( $title, $expected, $msg='' ) {
298 $title = Title::newFromText( $title );
299 $this->assertEquals( $expected,
300 $title->getRootText(),
301 $msg
302 );
303 }
304
305 public static function provideRootTitleCases() {
306 return array(
307 # Title, expected base, optional message
308 array('User:John_Doe/subOne/subTwo', 'John Doe' ),
309 array('User:Foo/Bar/Baz', 'Foo' ),
310 );
311 }
312
313 /**
314 * @todo Handle $wgNamespacesWithSubpages cases
315 * @dataProvider provideSubpageTitleCases
316 */
317 function testExtractingSubpageTextFromTitle( $title, $expected, $msg='' ) {
318 $title = Title::newFromText( $title );
319 $this->assertEquals( $expected,
320 $title->getSubpageText(),
321 $msg
322 );
323 }
324
325 function provideSubpageTitleCases() {
326 return array(
327 # Title, expected base, optional message
328 array('User:John_Doe/subOne/subTwo', 'subTwo' ),
329 array('User:John_Doe/subOne', 'subOne' ),
330 );
331 }
332
333 }