Merge "HTML escape parameter 'text' of hook 'SkinEditSectionLinks'"
[lhc/web/wiklou.git] / tests / phpunit / includes / title / NamespaceInfoTest.php
1 <?php
2 /**
3 * @author Antoine Musso
4 * @copyright Copyright © 2011, Antoine Musso
5 * @file
6 */
7
8 use MediaWiki\Config\ServiceOptions;
9
10 class NamespaceInfoTest extends MediaWikiTestCase {
11 /**********************************************************************************************
12 * Shared code
13 * %{
14 */
15 private $scopedCallback;
16
17 public function setUp() {
18 parent::setUp();
19
20 // Boo, there's still some global state in the class :(
21 global $wgHooks;
22 $hooks = $wgHooks;
23 unset( $hooks['CanonicalNamespaces'] );
24 $this->setMwGlobals( 'wgHooks', $hooks );
25
26 $this->scopedCallback =
27 ExtensionRegistry::getInstance()->setAttributeForTest( 'ExtensionNamespaces', [] );
28 }
29
30 public function tearDown() {
31 $this->scopedCallback = null;
32
33 parent::tearDown();
34 }
35
36 /**
37 * TODO Make this a const once HHVM support is dropped (T192166)
38 */
39 private static $defaultOptions = [
40 'AllowImageMoving' => true,
41 'CanonicalNamespaceNames' => [
42 NS_TALK => 'Talk',
43 NS_USER => 'User',
44 NS_USER_TALK => 'User_talk',
45 NS_SPECIAL => 'Special',
46 NS_MEDIA => 'Media',
47 ],
48 'CapitalLinkOverrides' => [],
49 'CapitalLinks' => true,
50 'ContentNamespaces' => [ NS_MAIN ],
51 'ExtraNamespaces' => [],
52 'ExtraSignatureNamespaces' => [],
53 'NamespaceContentModels' => [],
54 'NamespaceProtection' => [],
55 'NamespacesWithSubpages' => [
56 NS_TALK => true,
57 NS_USER => true,
58 NS_USER_TALK => true,
59 ],
60 'NonincludableNamespaces' => [],
61 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop' ],
62 ];
63
64 private function newObj( array $options = [] ) : NamespaceInfo {
65 return new NamespaceInfo( new ServiceOptions( NamespaceInfo::$constructorOptions,
66 $options, self::$defaultOptions ) );
67 }
68
69 // %} End shared code
70
71 /**********************************************************************************************
72 * Basic methods
73 * %{
74 */
75
76 /**
77 * @covers NamespaceInfo::__construct
78 * @dataProvider provideConstructor
79 * @param ServiceOptions $options
80 * @param string|null $expectedExceptionText
81 */
82 public function testConstructor( ServiceOptions $options, $expectedExceptionText = null ) {
83 if ( $expectedExceptionText !== null ) {
84 $this->setExpectedException( \Wikimedia\Assert\PreconditionException::class,
85 $expectedExceptionText );
86 }
87 new NamespaceInfo( $options );
88 $this->assertTrue( true );
89 }
90
91 public function provideConstructor() {
92 return [
93 [ new ServiceOptions( NamespaceInfo::$constructorOptions, self::$defaultOptions ) ],
94 [ new ServiceOptions( [], [] ), 'Required options missing: ' ],
95 [ new ServiceOptions(
96 array_merge( NamespaceInfo::$constructorOptions, [ 'invalid' ] ),
97 self::$defaultOptions,
98 [ 'invalid' => '' ]
99 ), 'Unsupported options passed: invalid' ],
100 ];
101 }
102
103 /**
104 * @dataProvider provideIsMovable
105 * @covers NamespaceInfo::isMovable
106 *
107 * @param bool $expected
108 * @param int $ns
109 * @param bool $allowImageMoving
110 */
111 public function testIsMovable( $expected, $ns, $allowImageMoving = true ) {
112 $obj = $this->newObj( [ 'AllowImageMoving' => $allowImageMoving ] );
113 $this->assertSame( $expected, $obj->isMovable( $ns ) );
114 }
115
116 public function provideIsMovable() {
117 return [
118 'Main' => [ true, NS_MAIN ],
119 'Talk' => [ true, NS_TALK ],
120 'Special' => [ false, NS_SPECIAL ],
121 'Nonexistent even namespace' => [ true, 1234 ],
122 'Nonexistent odd namespace' => [ true, 12345 ],
123
124 'Media with image moving' => [ false, NS_MEDIA, true ],
125 'Media with no image moving' => [ false, NS_MEDIA, false ],
126 'File with image moving' => [ true, NS_FILE, true ],
127 'File with no image moving' => [ false, NS_FILE, false ],
128 ];
129 }
130
131 /**
132 * @param int $ns
133 * @param bool $expected
134 * @dataProvider provideIsSubject
135 * @covers NamespaceInfo::isSubject
136 */
137 public function testIsSubject( $ns, $expected ) {
138 $this->assertSame( $expected, $this->newObj()->isSubject( $ns ) );
139 }
140
141 /**
142 * @param int $ns
143 * @param bool $expected
144 * @dataProvider provideIsSubject
145 * @covers NamespaceInfo::isTalk
146 */
147 public function testIsTalk( $ns, $expected ) {
148 $this->assertSame( !$expected, $this->newObj()->isTalk( $ns ) );
149 }
150
151 public function provideIsSubject() {
152 return [
153 // Special namespaces
154 [ NS_MEDIA, true ],
155 [ NS_SPECIAL, true ],
156
157 // Subject pages
158 [ NS_MAIN, true ],
159 [ NS_USER, true ],
160 [ 100, true ],
161
162 // Talk pages
163 [ NS_TALK, false ],
164 [ NS_USER_TALK, false ],
165 [ 101, false ],
166 ];
167 }
168
169 /**
170 * @covers NamespaceInfo::exists
171 * @dataProvider provideExists
172 * @param int $ns
173 * @param bool $expected
174 */
175 public function testExists( $ns, $expected ) {
176 $this->assertSame( $expected, $this->newObj()->exists( $ns ) );
177 }
178
179 public function provideExists() {
180 return [
181 'Main' => [ NS_MAIN, true ],
182 'Talk' => [ NS_TALK, true ],
183 'Media' => [ NS_MEDIA, true ],
184 'Special' => [ NS_SPECIAL, true ],
185 'Nonexistent' => [ 12345, false ],
186 'Negative nonexistent' => [ -12345, false ],
187 ];
188 }
189
190 /**
191 * Note if we add a namespace registration system with keys like 'MAIN'
192 * we should add tests here for equivalence on things like 'MAIN' == 0
193 * and 'MAIN' == NS_MAIN.
194 * @covers NamespaceInfo::equals
195 */
196 public function testEquals() {
197 $obj = $this->newObj();
198 $this->assertTrue( $obj->equals( NS_MAIN, NS_MAIN ) );
199 $this->assertTrue( $obj->equals( NS_MAIN, 0 ) ); // In case we make NS_MAIN 'MAIN'
200 $this->assertTrue( $obj->equals( NS_USER, NS_USER ) );
201 $this->assertTrue( $obj->equals( NS_USER, 2 ) );
202 $this->assertTrue( $obj->equals( NS_USER_TALK, NS_USER_TALK ) );
203 $this->assertTrue( $obj->equals( NS_SPECIAL, NS_SPECIAL ) );
204 $this->assertFalse( $obj->equals( NS_MAIN, NS_TALK ) );
205 $this->assertFalse( $obj->equals( NS_USER, NS_USER_TALK ) );
206 $this->assertFalse( $obj->equals( NS_PROJECT, NS_TEMPLATE ) );
207 }
208
209 /**
210 * @param int $ns1
211 * @param int $ns2
212 * @param bool $expected
213 * @dataProvider provideSubjectEquals
214 * @covers NamespaceInfo::subjectEquals
215 */
216 public function testSubjectEquals( $ns1, $ns2, $expected ) {
217 $this->assertSame( $expected, $this->newObj()->subjectEquals( $ns1, $ns2 ) );
218 }
219
220 public function provideSubjectEquals() {
221 return [
222 [ NS_MAIN, NS_MAIN, true ],
223 // In case we make NS_MAIN 'MAIN'
224 [ NS_MAIN, 0, true ],
225 [ NS_USER, NS_USER, true ],
226 [ NS_USER, 2, true ],
227 [ NS_USER_TALK, NS_USER_TALK, true ],
228 [ NS_SPECIAL, NS_SPECIAL, true ],
229 [ NS_MAIN, NS_TALK, true ],
230 [ NS_USER, NS_USER_TALK, true ],
231
232 [ NS_PROJECT, NS_TEMPLATE, false ],
233 [ NS_SPECIAL, NS_MAIN, false ],
234 [ NS_MEDIA, NS_SPECIAL, false ],
235 [ NS_SPECIAL, NS_MEDIA, false ],
236 ];
237 }
238
239 /**
240 * @dataProvider provideHasTalkNamespace
241 * @covers NamespaceInfo::hasTalkNamespace
242 *
243 * @param int $ns
244 * @param bool $expected
245 */
246 public function testHasTalkNamespace( $ns, $expected ) {
247 $this->assertSame( $expected, $this->newObj()->hasTalkNamespace( $ns ) );
248 }
249
250 public function provideHasTalkNamespace() {
251 return [
252 [ NS_MEDIA, false ],
253 [ NS_SPECIAL, false ],
254
255 [ NS_MAIN, true ],
256 [ NS_TALK, true ],
257 [ NS_USER, true ],
258 [ NS_USER_TALK, true ],
259
260 [ 100, true ],
261 [ 101, true ],
262 ];
263 }
264
265 /**
266 * @param int $ns
267 * @param bool $expected
268 * @param array $contentNamespaces
269 * @covers NamespaceInfo::isContent
270 * @dataProvider provideIsContent
271 */
272 public function testIsContent( $ns, $expected, $contentNamespaces = [ NS_MAIN ] ) {
273 $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
274 $this->assertSame( $expected, $obj->isContent( $ns ) );
275 }
276
277 public function provideIsContent() {
278 return [
279 [ NS_MAIN, true ],
280 [ NS_MEDIA, false ],
281 [ NS_SPECIAL, false ],
282 [ NS_TALK, false ],
283 [ NS_USER, false ],
284 [ NS_CATEGORY, false ],
285 [ 100, false ],
286 [ 100, true, [ NS_MAIN, 100, 252 ] ],
287 [ 252, true, [ NS_MAIN, 100, 252 ] ],
288 [ NS_MAIN, true, [ NS_MAIN, 100, 252 ] ],
289 // NS_MAIN is always content
290 [ NS_MAIN, true, [] ],
291 ];
292 }
293
294 /**
295 * @dataProvider provideWantSignatures
296 * @covers NamespaceInfo::wantSignatures
297 *
298 * @param int $index
299 * @param bool $expected
300 */
301 public function testWantSignatures( $index, $expected ) {
302 $this->assertSame( $expected, $this->newObj()->wantSignatures( $index ) );
303 }
304
305 public function provideWantSignatures() {
306 return [
307 'Main' => [ NS_MAIN, false ],
308 'Talk' => [ NS_TALK, true ],
309 'User' => [ NS_USER, false ],
310 'User talk' => [ NS_USER_TALK, true ],
311 'Special' => [ NS_SPECIAL, false ],
312 'Media' => [ NS_MEDIA, false ],
313 'Nonexistent talk' => [ 12345, true ],
314 'Nonexistent subject' => [ 123456, false ],
315 'Nonexistent negative odd' => [ -12345, false ],
316 ];
317 }
318
319 /**
320 * @dataProvider provideWantSignatures_ExtraSignatureNamespaces
321 * @covers NamespaceInfo::wantSignatures
322 *
323 * @param int $index
324 * @param int $expected
325 */
326 public function testWantSignatures_ExtraSignatureNamespaces( $index, $expected ) {
327 $obj = $this->newObj( [ 'ExtraSignatureNamespaces' =>
328 [ NS_MAIN, NS_USER, NS_SPECIAL, NS_MEDIA, 123456, -12345 ] ] );
329 $this->assertSame( $expected, $obj->wantSignatures( $index ) );
330 }
331
332 public function provideWantSignatures_ExtraSignatureNamespaces() {
333 $ret = array_map(
334 function ( $arr ) {
335 // We've added all these as extra signature namespaces, so expect true
336 return [ $arr[0], true ];
337 },
338 self::provideWantSignatures()
339 );
340
341 // Add one more that's false
342 $ret['Another nonexistent subject'] = [ 12345678, false ];
343 return $ret;
344 }
345
346 /**
347 * @param int $ns
348 * @param bool $expected
349 * @covers NamespaceInfo::isWatchable
350 * @dataProvider provideIsWatchable
351 */
352 public function testIsWatchable( $ns, $expected ) {
353 $this->assertSame( $expected, $this->newObj()->isWatchable( $ns ) );
354 }
355
356 public function provideIsWatchable() {
357 return [
358 // Specials namespaces are not watchable
359 [ NS_MEDIA, false ],
360 [ NS_SPECIAL, false ],
361
362 // Core defined namespaces are watchables
363 [ NS_MAIN, true ],
364 [ NS_TALK, true ],
365
366 // Additional, user defined namespaces are watchables
367 [ 100, true ],
368 [ 101, true ],
369 ];
370 }
371
372 /**
373 * @param int $ns
374 * @param int $expected
375 * @param array|null $namespacesWithSubpages To pass to constructor
376 * @covers NamespaceInfo::hasSubpages
377 * @dataProvider provideHasSubpages
378 */
379 public function testHasSubpages( $ns, $expected, array $namespacesWithSubpages = null ) {
380 $obj = $this->newObj( $namespacesWithSubpages
381 ? [ 'NamespacesWithSubpages' => $namespacesWithSubpages ]
382 : [] );
383 $this->assertSame( $expected, $obj->hasSubpages( $ns ) );
384 }
385
386 public function provideHasSubpages() {
387 return [
388 // Special namespaces:
389 [ NS_MEDIA, false ],
390 [ NS_SPECIAL, false ],
391
392 // Namespaces without subpages
393 [ NS_MAIN, false ],
394 [ NS_MAIN, true, [ NS_MAIN => true ] ],
395 [ NS_MAIN, false, [ NS_MAIN => false ] ],
396
397 // Some namespaces with subpages
398 [ NS_TALK, true ],
399 [ NS_USER, true ],
400 [ NS_USER_TALK, true ],
401 ];
402 }
403
404 /**
405 * @param $contentNamespaces To pass to constructor
406 * @param array $expected
407 * @dataProvider provideGetContentNamespaces
408 * @covers NamespaceInfo::getContentNamespaces
409 */
410 public function testGetContentNamespaces( $contentNamespaces, array $expected ) {
411 $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
412 $this->assertSame( $expected, $obj->getContentNamespaces() );
413 }
414
415 public function provideGetContentNamespaces() {
416 return [
417 // Non-array
418 [ '', [ NS_MAIN ] ],
419 [ false, [ NS_MAIN ] ],
420 [ null, [ NS_MAIN ] ],
421 [ 5, [ NS_MAIN ] ],
422
423 // Empty array
424 [ [], [ NS_MAIN ] ],
425
426 // NS_MAIN is forced to be content even if unwanted
427 [ [ NS_USER, NS_CATEGORY ], [ NS_MAIN, NS_USER, NS_CATEGORY ] ],
428
429 // In other cases, return as-is
430 [ [ NS_MAIN ], [ NS_MAIN ] ],
431 [ [ NS_MAIN, NS_USER, NS_CATEGORY ], [ NS_MAIN, NS_USER, NS_CATEGORY ] ],
432 ];
433 }
434
435 /**
436 * @covers NamespaceInfo::getSubjectNamespaces
437 */
438 public function testGetSubjectNamespaces() {
439 $subjectsNS = $this->newObj()->getSubjectNamespaces();
440 $this->assertContains( NS_MAIN, $subjectsNS,
441 "Talk namespaces should have NS_MAIN" );
442 $this->assertNotContains( NS_TALK, $subjectsNS,
443 "Talk namespaces should have NS_TALK" );
444
445 $this->assertNotContains( NS_MEDIA, $subjectsNS,
446 "Talk namespaces should not have NS_MEDIA" );
447 $this->assertNotContains( NS_SPECIAL, $subjectsNS,
448 "Talk namespaces should not have NS_SPECIAL" );
449 }
450
451 /**
452 * @covers NamespaceInfo::getTalkNamespaces
453 */
454 public function testGetTalkNamespaces() {
455 $talkNS = $this->newObj()->getTalkNamespaces();
456 $this->assertContains( NS_TALK, $talkNS,
457 "Subject namespaces should have NS_TALK" );
458 $this->assertNotContains( NS_MAIN, $talkNS,
459 "Subject namespaces should not have NS_MAIN" );
460
461 $this->assertNotContains( NS_MEDIA, $talkNS,
462 "Subject namespaces should not have NS_MEDIA" );
463 $this->assertNotContains( NS_SPECIAL, $talkNS,
464 "Subject namespaces should not have NS_SPECIAL" );
465 }
466
467 /**
468 * @param int $ns
469 * @param bool $expected
470 * @param bool $capitalLinks To pass to constructor
471 * @param array $capitalLinkOverrides To pass to constructor
472 * @dataProvider provideIsCapitalized
473 * @covers NamespaceInfo::isCapitalized
474 */
475 public function testIsCapitalized(
476 $ns, $expected, $capitalLinks = true, array $capitalLinkOverrides = []
477 ) {
478 $obj = $this->newObj( [
479 'CapitalLinks' => $capitalLinks,
480 'CapitalLinkOverrides' => $capitalLinkOverrides,
481 ] );
482 $this->assertSame( $expected, $obj->isCapitalized( $ns ) );
483 }
484
485 public function provideIsCapitalized() {
486 return [
487 // Test default settings
488 [ NS_PROJECT, true ],
489 [ NS_PROJECT_TALK, true ],
490 [ NS_MEDIA, true ],
491 [ NS_FILE, true ],
492
493 // Always capitalized no matter what
494 [ NS_SPECIAL, true, false ],
495 [ NS_USER, true, false ],
496 [ NS_MEDIAWIKI, true, false ],
497
498 // Even with an override too
499 [ NS_SPECIAL, true, false, [ NS_SPECIAL => false ] ],
500 [ NS_USER, true, false, [ NS_USER => false ] ],
501 [ NS_MEDIAWIKI, true, false, [ NS_MEDIAWIKI => false ] ],
502
503 // Overrides work for other namespaces
504 [ NS_PROJECT, false, true, [ NS_PROJECT => false ] ],
505 [ NS_PROJECT, true, false, [ NS_PROJECT => true ] ],
506
507 // NS_MEDIA is treated like NS_FILE, and ignores NS_MEDIA overrides
508 [ NS_MEDIA, false, true, [ NS_FILE => false, NS_MEDIA => true ] ],
509 [ NS_MEDIA, true, false, [ NS_FILE => true, NS_MEDIA => false ] ],
510 [ NS_FILE, false, true, [ NS_FILE => false, NS_MEDIA => true ] ],
511 [ NS_FILE, true, false, [ NS_FILE => true, NS_MEDIA => false ] ],
512 ];
513 }
514
515 /**
516 * @covers NamespaceInfo::hasGenderDistinction
517 */
518 public function testHasGenderDistinction() {
519 $obj = $this->newObj();
520
521 // Namespaces with gender distinctions
522 $this->assertTrue( $obj->hasGenderDistinction( NS_USER ) );
523 $this->assertTrue( $obj->hasGenderDistinction( NS_USER_TALK ) );
524
525 // Other ones, "genderless"
526 $this->assertFalse( $obj->hasGenderDistinction( NS_MEDIA ) );
527 $this->assertFalse( $obj->hasGenderDistinction( NS_SPECIAL ) );
528 $this->assertFalse( $obj->hasGenderDistinction( NS_MAIN ) );
529 $this->assertFalse( $obj->hasGenderDistinction( NS_TALK ) );
530 }
531
532 /**
533 * @covers NamespaceInfo::isNonincludable
534 */
535 public function testIsNonincludable() {
536 $obj = $this->newObj( [ 'NonincludableNamespaces' => [ NS_USER ] ] );
537 $this->assertTrue( $obj->isNonincludable( NS_USER ) );
538 $this->assertFalse( $obj->isNonincludable( NS_TEMPLATE ) );
539 }
540
541 /**
542 * @dataProvider provideGetNamespaceContentModel
543 * @covers NamespaceInfo::getNamespaceContentModel
544 *
545 * @param int $ns
546 * @param string $expected
547 */
548 public function testGetNamespaceContentModel( $ns, $expected ) {
549 $obj = $this->newObj( [ 'NamespaceContentModels' =>
550 [ NS_USER => CONTENT_MODEL_WIKITEXT, 123 => CONTENT_MODEL_JSON, 1234 => 'abcdef' ],
551 ] );
552 $this->assertSame( $expected, $obj->getNamespaceContentModel( $ns ) );
553 }
554
555 public function provideGetNamespaceContentModel() {
556 return [
557 [ NS_MAIN, null ],
558 [ NS_TALK, null ],
559 [ NS_USER, CONTENT_MODEL_WIKITEXT ],
560 [ NS_USER_TALK, null ],
561 [ NS_SPECIAL, null ],
562 [ 122, null ],
563 [ 123, CONTENT_MODEL_JSON ],
564 [ 1234, 'abcdef' ],
565 [ 1235, null ],
566 ];
567 }
568
569 /**
570 * @dataProvider provideGetCategoryLinkType
571 * @covers NamespaceInfo::getCategoryLinkType
572 *
573 * @param int $ns
574 * @param string $expected
575 */
576 public function testGetCategoryLinkType( $ns, $expected ) {
577 $this->assertSame( $expected, $this->newObj()->getCategoryLinkType( $ns ) );
578 }
579
580 public function provideGetCategoryLinkType() {
581 return [
582 [ NS_MAIN, 'page' ],
583 [ NS_TALK, 'page' ],
584 [ NS_USER, 'page' ],
585 [ NS_USER_TALK, 'page' ],
586
587 [ NS_FILE, 'file' ],
588 [ NS_FILE_TALK, 'page' ],
589
590 [ NS_CATEGORY, 'subcat' ],
591 [ NS_CATEGORY_TALK, 'page' ],
592
593 [ 100, 'page' ],
594 [ 101, 'page' ],
595 ];
596 }
597
598 // %} End basic methods
599
600 /**********************************************************************************************
601 * getSubject/Talk/Associated
602 * %{
603 */
604 /**
605 * @dataProvider provideSubjectTalk
606 * @covers NamespaceInfo::getSubject
607 * @covers NamespaceInfo::getSubjectPage
608 * @covers NamespaceInfo::isMethodValidFor
609 * @covers Title::getSubjectPage
610 *
611 * @param int $subject
612 * @param int $talk
613 */
614 public function testGetSubject( $subject, $talk ) {
615 $obj = $this->newObj();
616 $this->assertSame( $subject, $obj->getSubject( $subject ) );
617 $this->assertSame( $subject, $obj->getSubject( $talk ) );
618
619 $subjectTitleVal = new TitleValue( $subject, 'A' );
620 $talkTitleVal = new TitleValue( $talk, 'A' );
621 // Object will be the same one passed in if it's a subject, different but equal object if
622 // it's talk
623 $this->assertSame( $subjectTitleVal, $obj->getSubjectPage( $subjectTitleVal ) );
624 $this->assertEquals( $subjectTitleVal, $obj->getSubjectPage( $talkTitleVal ) );
625
626 $subjectTitle = Title::makeTitle( $subject, 'A' );
627 $talkTitle = Title::makeTitle( $talk, 'A' );
628 $this->assertSame( $subjectTitle, $subjectTitle->getSubjectPage() );
629 $this->assertEquals( $subjectTitle, $talkTitle->getSubjectPage() );
630 }
631
632 /**
633 * @dataProvider provideSpecialNamespaces
634 * @covers NamespaceInfo::getSubject
635 * @covers NamespaceInfo::getSubjectPage
636 *
637 * @param int $ns
638 */
639 public function testGetSubject_special( $ns ) {
640 $obj = $this->newObj();
641 $this->assertSame( $ns, $obj->getSubject( $ns ) );
642
643 $title = new TitleValue( $ns, 'A' );
644 $this->assertSame( $title, $obj->getSubjectPage( $title ) );
645 }
646
647 /**
648 * @dataProvider provideSubjectTalk
649 * @covers NamespaceInfo::getTalk
650 * @covers NamespaceInfo::getTalkPage
651 * @covers NamespaceInfo::isMethodValidFor
652 * @covers Title::getTalkPage
653 *
654 * @param int $subject
655 * @param int $talk
656 */
657 public function testGetTalk( $subject, $talk ) {
658 $obj = $this->newObj();
659 $this->assertSame( $talk, $obj->getTalk( $subject ) );
660 $this->assertSame( $talk, $obj->getTalk( $talk ) );
661
662 $subjectTitleVal = new TitleValue( $subject, 'A' );
663 $talkTitleVal = new TitleValue( $talk, 'A' );
664 // Object will be the same one passed in if it's a talk, different but equal object if it's
665 // subject
666 $this->assertEquals( $talkTitleVal, $obj->getTalkPage( $subjectTitleVal ) );
667 $this->assertSame( $talkTitleVal, $obj->getTalkPage( $talkTitleVal ) );
668
669 $subjectTitle = Title::makeTitle( $subject, 'A' );
670 $talkTitle = Title::makeTitle( $talk, 'A' );
671 $this->assertEquals( $talkTitle, $subjectTitle->getTalkPage() );
672 $this->assertSame( $talkTitle, $talkTitle->getTalkPage() );
673 }
674
675 /**
676 * @dataProvider provideSpecialNamespaces
677 * @covers NamespaceInfo::getTalk
678 * @covers NamespaceInfo::isMethodValidFor
679 *
680 * @param int $ns
681 */
682 public function testGetTalk_special( $ns ) {
683 $this->setExpectedException( MWException::class,
684 "NamespaceInfo::getTalk does not make any sense for given namespace $ns" );
685 $this->newObj()->getTalk( $ns );
686 }
687
688 /**
689 * @dataProvider provideSpecialNamespaces
690 * @covers NamespaceInfo::getTalk
691 * @covers NamespaceInfo::getTalkPage
692 * @covers NamespaceInfo::isMethodValidFor
693 *
694 * @param int $ns
695 */
696 public function testGetTalkPage_special( $ns ) {
697 $this->setExpectedException( MWException::class,
698 "NamespaceInfo::getTalk does not make any sense for given namespace $ns" );
699 $this->newObj()->getTalkPage( new TitleValue( $ns, 'A' ) );
700 }
701
702 /**
703 * @dataProvider provideSpecialNamespaces
704 * @covers NamespaceInfo::getTalk
705 * @covers NamespaceInfo::getTalkPage
706 * @covers NamespaceInfo::isMethodValidFor
707 * @covers Title::getTalkPage
708 *
709 * @param int $ns
710 */
711 public function testTitleGetTalkPage_special( $ns ) {
712 $this->setExpectedException( MWException::class,
713 "NamespaceInfo::getTalk does not make any sense for given namespace $ns" );
714 Title::makeTitle( $ns, 'A' )->getTalkPage();
715 }
716
717 /**
718 * @dataProvider provideSpecialNamespaces
719 * @covers NamespaceInfo::getAssociated
720 * @covers NamespaceInfo::isMethodValidFor
721 *
722 * @param int $ns
723 */
724 public function testGetAssociated_special( $ns ) {
725 $this->setExpectedException( MWException::class,
726 "NamespaceInfo::getAssociated does not make any sense for given namespace $ns" );
727 $this->newObj()->getAssociated( $ns );
728 }
729
730 /**
731 * @dataProvider provideSpecialNamespaces
732 * @covers NamespaceInfo::getAssociated
733 * @covers NamespaceInfo::getAssociatedPage
734 * @covers NamespaceInfo::isMethodValidFor
735 *
736 * @param int $ns
737 */
738 public function testGetAssociatedPage_special( $ns ) {
739 $this->setExpectedException( MWException::class,
740 "NamespaceInfo::getAssociated does not make any sense for given namespace $ns" );
741 $this->newObj()->getAssociatedPage( new TitleValue( $ns, 'A' ) );
742 }
743
744 /**
745 * @dataProvider provideSpecialNamespaces
746 * @covers NamespaceInfo::getAssociated
747 * @covers NamespaceInfo::getAssociatedPage
748 * @covers NamespaceInfo::isMethodValidFor
749 * @covers Title::getOtherPage
750 *
751 * @param int $ns
752 */
753 public function testTitleGetOtherPage_special( $ns ) {
754 $this->setExpectedException( MWException::class,
755 "NamespaceInfo::getAssociated does not make any sense for given namespace $ns" );
756 Title::makeTitle( $ns, 'A' )->getOtherPage();
757 }
758
759 /**
760 * @dataProvider provideSubjectTalk
761 * @covers NamespaceInfo::getAssociated
762 * @covers NamespaceInfo::getAssociatedPage
763 * @covers Title::getOtherPage
764 *
765 * @param int $subject
766 * @param int $talk
767 */
768 public function testGetAssociated( $subject, $talk ) {
769 $obj = $this->newObj();
770 $this->assertSame( $talk, $obj->getAssociated( $subject ) );
771 $this->assertSame( $subject, $obj->getAssociated( $talk ) );
772
773 $subjectTitle = new TitleValue( $subject, 'A' );
774 $talkTitle = new TitleValue( $talk, 'A' );
775 // Object will not be the same
776 $this->assertEquals( $talkTitle, $obj->getAssociatedPage( $subjectTitle ) );
777 $this->assertEquals( $subjectTitle, $obj->getAssociatedPage( $talkTitle ) );
778
779 $subjectTitle = Title::makeTitle( $subject, 'A' );
780 $talkTitle = Title::makeTitle( $talk, 'A' );
781 $this->assertEquals( $talkTitle, $subjectTitle->getOtherPage() );
782 $this->assertEquals( $subjectTitle, $talkTitle->getOtherPage() );
783 }
784
785 public static function provideSubjectTalk() {
786 return [
787 // Format: [ subject, talk ]
788 'Main/talk' => [ NS_MAIN, NS_TALK ],
789 'User/user talk' => [ NS_USER, NS_USER_TALK ],
790 'Unknown namespaces also supported' => [ 106, 107 ],
791 ];
792 }
793
794 public static function provideSpecialNamespaces() {
795 return [
796 'Special' => [ NS_SPECIAL ],
797 'Media' => [ NS_MEDIA ],
798 'Unknown negative index' => [ -613 ],
799 ];
800 }
801
802 // %} End getSubject/Talk/Associated
803
804 /**********************************************************************************************
805 * Canonical namespaces
806 * %{
807 */
808
809 // Default canonical namespaces
810 // %{
811 private function getDefaultNamespaces() {
812 return [ NS_MAIN => '' ] + self::$defaultOptions['CanonicalNamespaceNames'];
813 }
814
815 /**
816 * @covers NamespaceInfo::getCanonicalNamespaces
817 */
818 public function testGetCanonicalNamespaces() {
819 $this->assertSame(
820 $this->getDefaultNamespaces(),
821 $this->newObj()->getCanonicalNamespaces()
822 );
823 }
824
825 /**
826 * @dataProvider provideGetCanonicalName
827 * @covers NamespaceInfo::getCanonicalName
828 *
829 * @param int $index
830 * @param string|bool $expected
831 */
832 public function testGetCanonicalName( $index, $expected ) {
833 $this->assertSame( $expected, $this->newObj()->getCanonicalName( $index ) );
834 }
835
836 public function provideGetCanonicalName() {
837 return [
838 'Main' => [ NS_MAIN, '' ],
839 'Talk' => [ NS_TALK, 'Talk' ],
840 'With underscore not space' => [ NS_USER_TALK, 'User_talk' ],
841 'Special' => [ NS_SPECIAL, 'Special' ],
842 'Nonexistent' => [ 12345, false ],
843 'Nonexistent negative' => [ -12345, false ],
844 ];
845 }
846
847 /**
848 * @dataProvider provideGetCanonicalIndex
849 * @covers NamespaceInfo::getCanonicalIndex
850 *
851 * @param string $name
852 * @param int|null $expected
853 */
854 public function testGetCanonicalIndex( $name, $expected ) {
855 $this->assertSame( $expected, $this->newObj()->getCanonicalIndex( $name ) );
856 }
857
858 public function provideGetCanonicalIndex() {
859 return [
860 'Main' => [ '', NS_MAIN ],
861 'Talk' => [ 'talk', NS_TALK ],
862 'Not lowercase' => [ 'Talk', null ],
863 'With underscore' => [ 'user_talk', NS_USER_TALK ],
864 'Space is not recognized for underscore' => [ 'user talk', null ],
865 '0' => [ '0', null ],
866 ];
867 }
868
869 /**
870 * @covers NamespaceInfo::getValidNamespaces
871 */
872 public function testGetValidNamespaces() {
873 $this->assertSame(
874 [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK ],
875 $this->newObj()->getValidNamespaces()
876 );
877 }
878
879 // %} End default canonical namespaces
880
881 // No canonical namespace names
882 // %{
883 /**
884 * @covers NamespaceInfo::getCanonicalNamespaces
885 */
886 public function testGetCanonicalNamespaces_NoCanonicalNamespaceNames() {
887 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
888
889 $this->assertSame( [ NS_MAIN => '' ], $obj->getCanonicalNamespaces() );
890 }
891
892 /**
893 * @covers NamespaceInfo::getCanonicalName
894 */
895 public function testGetCanonicalName_NoCanonicalNamespaceNames() {
896 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
897
898 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
899 $this->assertFalse( $obj->getCanonicalName( NS_TALK ) );
900 }
901
902 /**
903 * @covers NamespaceInfo::getCanonicalIndex
904 */
905 public function testGetCanonicalIndex_NoCanonicalNamespaceNames() {
906 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
907
908 $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( '' ) );
909 $this->assertNull( $obj->getCanonicalIndex( 'talk' ) );
910 }
911
912 /**
913 * @covers NamespaceInfo::getValidNamespaces
914 */
915 public function testGetValidNamespaces_NoCanonicalNamespaceNames() {
916 $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
917
918 $this->assertSame( [ NS_MAIN ], $obj->getValidNamespaces() );
919 }
920
921 // %} End no canonical namespace names
922
923 // Test extension namespaces
924 // %{
925 private function setupExtensionNamespaces() {
926 $this->scopedCallback = null;
927 $this->scopedCallback = ExtensionRegistry::getInstance()->setAttributeForTest(
928 'ExtensionNamespaces',
929 [ NS_MAIN => 'No effect', NS_TALK => 'No effect', 12345 => 'Extended' ]
930 );
931 }
932
933 /**
934 * @covers NamespaceInfo::getCanonicalNamespaces
935 */
936 public function testGetCanonicalNamespaces_ExtensionNamespaces() {
937 $this->setupExtensionNamespaces();
938
939 $this->assertSame(
940 $this->getDefaultNamespaces() + [ 12345 => 'Extended' ],
941 $this->newObj()->getCanonicalNamespaces()
942 );
943 }
944
945 /**
946 * @covers NamespaceInfo::getCanonicalName
947 */
948 public function testGetCanonicalName_ExtensionNamespaces() {
949 $this->setupExtensionNamespaces();
950 $obj = $this->newObj();
951
952 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
953 $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK ) );
954 $this->assertSame( 'Extended', $obj->getCanonicalName( 12345 ) );
955 }
956
957 /**
958 * @covers NamespaceInfo::getCanonicalIndex
959 */
960 public function testGetCanonicalIndex_ExtensionNamespaces() {
961 $this->setupExtensionNamespaces();
962 $obj = $this->newObj();
963
964 $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( '' ) );
965 $this->assertSame( NS_TALK, $obj->getCanonicalIndex( 'talk' ) );
966 $this->assertSame( 12345, $obj->getCanonicalIndex( 'extended' ) );
967 }
968
969 /**
970 * @covers NamespaceInfo::getValidNamespaces
971 */
972 public function testGetValidNamespaces_ExtensionNamespaces() {
973 $this->setupExtensionNamespaces();
974
975 $this->assertSame(
976 [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 12345 ],
977 $this->newObj()->getValidNamespaces()
978 );
979 }
980
981 // %} End extension namespaces
982
983 // Hook namespaces
984 // %{
985 /**
986 * @return array Expected canonical namespaces
987 */
988 private function setupHookNamespaces() {
989 $callback =
990 function ( &$canonicalNamespaces ) {
991 $canonicalNamespaces[NS_MAIN] = 'Main';
992 unset( $canonicalNamespaces[NS_MEDIA] );
993 $canonicalNamespaces[123456] = 'Hooked';
994 };
995 $this->setTemporaryHook( 'CanonicalNamespaces', $callback );
996 $expected = $this->getDefaultNamespaces();
997 ( $callback )( $expected );
998 return $expected;
999 }
1000
1001 /**
1002 * @covers NamespaceInfo::getCanonicalNamespaces
1003 */
1004 public function testGetCanonicalNamespaces_HookNamespaces() {
1005 $expected = $this->setupHookNamespaces();
1006
1007 $this->assertSame( $expected, $this->newObj()->getCanonicalNamespaces() );
1008 }
1009
1010 /**
1011 * @covers NamespaceInfo::getCanonicalName
1012 */
1013 public function testGetCanonicalName_HookNamespaces() {
1014 $this->setupHookNamespaces();
1015 $obj = $this->newObj();
1016
1017 $this->assertSame( 'Main', $obj->getCanonicalName( NS_MAIN ) );
1018 $this->assertFalse( $obj->getCanonicalName( NS_MEDIA ) );
1019 $this->assertSame( 'Hooked', $obj->getCanonicalName( 123456 ) );
1020 }
1021
1022 /**
1023 * @covers NamespaceInfo::getCanonicalIndex
1024 */
1025 public function testGetCanonicalIndex_HookNamespaces() {
1026 $this->setupHookNamespaces();
1027 $obj = $this->newObj();
1028
1029 $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( 'main' ) );
1030 $this->assertNull( $obj->getCanonicalIndex( 'media' ) );
1031 $this->assertSame( 123456, $obj->getCanonicalIndex( 'hooked' ) );
1032 }
1033
1034 /**
1035 * @covers NamespaceInfo::getValidNamespaces
1036 */
1037 public function testGetValidNamespaces_HookNamespaces() {
1038 $this->setupHookNamespaces();
1039
1040 $this->assertSame(
1041 [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 123456 ],
1042 $this->newObj()->getValidNamespaces()
1043 );
1044 }
1045
1046 // %} End hook namespaces
1047
1048 // Extra namespaces
1049 // %{
1050 /**
1051 * @return NamespaceInfo
1052 */
1053 private function setupExtraNamespaces() {
1054 return $this->newObj( [ 'ExtraNamespaces' =>
1055 [ NS_MAIN => 'No effect', NS_TALK => 'No effect', 1234567 => 'Extra' ]
1056 ] );
1057 }
1058
1059 /**
1060 * @covers NamespaceInfo::getCanonicalNamespaces
1061 */
1062 public function testGetCanonicalNamespaces_ExtraNamespaces() {
1063 $this->assertSame(
1064 $this->getDefaultNamespaces() + [ 1234567 => 'Extra' ],
1065 $this->setupExtraNamespaces()->getCanonicalNamespaces()
1066 );
1067 }
1068
1069 /**
1070 * @covers NamespaceInfo::getCanonicalName
1071 */
1072 public function testGetCanonicalName_ExtraNamespaces() {
1073 $obj = $this->setupExtraNamespaces();
1074
1075 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
1076 $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK ) );
1077 $this->assertSame( 'Extra', $obj->getCanonicalName( 1234567 ) );
1078 }
1079
1080 /**
1081 * @covers NamespaceInfo::getCanonicalIndex
1082 */
1083 public function testGetCanonicalIndex_ExtraNamespaces() {
1084 $obj = $this->setupExtraNamespaces();
1085
1086 $this->assertNull( $obj->getCanonicalIndex( 'no effect' ) );
1087 $this->assertNull( $obj->getCanonicalIndex( 'no_effect' ) );
1088 $this->assertSame( 1234567, $obj->getCanonicalIndex( 'extra' ) );
1089 }
1090
1091 /**
1092 * @covers NamespaceInfo::getValidNamespaces
1093 */
1094 public function testGetValidNamespaces_ExtraNamespaces() {
1095 $this->assertSame(
1096 [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 1234567 ],
1097 $this->setupExtraNamespaces()->getValidNamespaces()
1098 );
1099 }
1100
1101 // %} End extra namespaces
1102
1103 // Canonical namespace caching
1104 // %{
1105 /**
1106 * @covers NamespaceInfo::getCanonicalNamespaces
1107 */
1108 public function testGetCanonicalNamespaces_caching() {
1109 $obj = $this->newObj();
1110
1111 // This should cache the values
1112 $obj->getCanonicalNamespaces();
1113
1114 // Now try to alter them through nefarious means
1115 $this->setupExtensionNamespaces();
1116 $this->setupHookNamespaces();
1117
1118 // Should have no effect
1119 $this->assertSame( $this->getDefaultNamespaces(), $obj->getCanonicalNamespaces() );
1120 }
1121
1122 /**
1123 * @covers NamespaceInfo::getCanonicalName
1124 */
1125 public function testGetCanonicalName_caching() {
1126 $obj = $this->newObj();
1127
1128 // This should cache the values
1129 $obj->getCanonicalName( NS_MAIN );
1130
1131 // Now try to alter them through nefarious means
1132 $this->setupExtensionNamespaces();
1133 $this->setupHookNamespaces();
1134
1135 // Should have no effect
1136 $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
1137 $this->assertSame( 'Media', $obj->getCanonicalName( NS_MEDIA ) );
1138 $this->assertFalse( $obj->getCanonicalName( 12345 ) );
1139 $this->assertFalse( $obj->getCanonicalName( 123456 ) );
1140 }
1141
1142 /**
1143 * @covers NamespaceInfo::getCanonicalIndex
1144 */
1145 public function testGetCanonicalIndex_caching() {
1146 $obj = $this->newObj();
1147
1148 // This should cache the values
1149 $obj->getCanonicalIndex( '' );
1150
1151 // Now try to alter them through nefarious means
1152 $this->setupExtensionNamespaces();
1153 $this->setupHookNamespaces();
1154
1155 // Should have no effect
1156 $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( '' ) );
1157 $this->assertSame( NS_MEDIA, $obj->getCanonicalIndex( 'media' ) );
1158 $this->assertNull( $obj->getCanonicalIndex( 'extended' ) );
1159 $this->assertNull( $obj->getCanonicalIndex( 'hooked' ) );
1160 }
1161
1162 /**
1163 * @covers NamespaceInfo::getValidNamespaces
1164 */
1165 public function testGetValidNamespaces_caching() {
1166 $obj = $this->newObj();
1167
1168 // This should cache the values
1169 $obj->getValidNamespaces();
1170
1171 // Now try to alter through nefarious means
1172 $this->setupExtensionNamespaces();
1173 $this->setupHookNamespaces();
1174
1175 // Should have no effect
1176 $this->assertSame(
1177 [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK ],
1178 $obj->getValidNamespaces()
1179 );
1180 }
1181
1182 // %} End canonical namespace caching
1183
1184 // Miscellaneous
1185 // %{
1186
1187 /**
1188 * @dataProvider provideGetValidNamespaces_misc
1189 * @covers NamespaceInfo::getValidNamespaces
1190 *
1191 * @param array $namespaces List of namespace indices to return from getCanonicalNamespaces()
1192 * (list is overwritten by a hook, so NS_MAIN doesn't have to be present)
1193 * @param array $expected
1194 */
1195 public function testGetValidNamespaces_misc( array $namespaces, array $expected ) {
1196 // Each namespace's name is just its index
1197 $this->setTemporaryHook( 'CanonicalNamespaces',
1198 function ( &$canonicalNamespaces ) use ( $namespaces ) {
1199 $canonicalNamespaces = array_combine( $namespaces, $namespaces );
1200 }
1201 );
1202 $this->assertSame( $expected, $this->newObj()->getValidNamespaces() );
1203 }
1204
1205 public function provideGetValidNamespaces_misc() {
1206 return [
1207 'Out of order (T109137)' => [ [ 1, 0 ], [ 0, 1 ] ],
1208 'Alphabetical order' => [ [ 10, 2 ], [ 2, 10 ] ],
1209 'Negative' => [ [ -1000, -500, -2, 0 ], [ 0 ] ],
1210 ];
1211 }
1212
1213 // %} End miscellaneous
1214 // %} End canonical namespaces
1215
1216 /**********************************************************************************************
1217 * Restriction levels
1218 * %{
1219 */
1220
1221 /**
1222 * This mock user can only have isAllowed() called on it.
1223 *
1224 * @param array $groups Groups for the mock user to have
1225 * @return User
1226 */
1227 private function getMockUser( array $groups = [] ) : User {
1228 $groups[] = '*';
1229
1230 $mock = $this->createMock( User::class );
1231 $mock->method( 'isAllowed' )->will( $this->returnCallback(
1232 function ( $action ) use ( $groups ) {
1233 global $wgGroupPermissions, $wgRevokePermissions;
1234 if ( $action == '' ) {
1235 return true;
1236 }
1237 foreach ( $wgRevokePermissions as $group => $rights ) {
1238 if ( !in_array( $group, $groups ) ) {
1239 continue;
1240 }
1241 if ( isset( $rights[$action] ) && $rights[$action] ) {
1242 return false;
1243 }
1244 }
1245 foreach ( $wgGroupPermissions as $group => $rights ) {
1246 if ( !in_array( $group, $groups ) ) {
1247 continue;
1248 }
1249 if ( isset( $rights[$action] ) && $rights[$action] ) {
1250 return true;
1251 }
1252 }
1253 return false;
1254 }
1255 ) );
1256 $mock->expects( $this->never() )->method( $this->anythingBut( 'isAllowed' ) );
1257 return $mock;
1258 }
1259
1260 /**
1261 * @dataProvider provideGetRestrictionLevels
1262 * @covers NamespaceInfo::getRestrictionLevels
1263 *
1264 * @param array $expected
1265 * @param int $ns
1266 * @param User|null $user
1267 */
1268 public function testGetRestrictionLevels( array $expected, $ns, User $user = null ) {
1269 $this->setMwGlobals( [
1270 'wgGroupPermissions' => [
1271 '*' => [ 'edit' => true ],
1272 'autoconfirmed' => [ 'editsemiprotected' => true ],
1273 'sysop' => [
1274 'editsemiprotected' => true,
1275 'editprotected' => true,
1276 ],
1277 'privileged' => [ 'privileged' => true ],
1278 ],
1279 'wgRevokePermissions' => [
1280 'noeditsemiprotected' => [ 'editsemiprotected' => true ],
1281 ],
1282 ] );
1283 $obj = $this->newObj( [
1284 'NamespaceProtection' => [
1285 NS_MAIN => 'autoconfirmed',
1286 NS_USER => 'sysop',
1287 101 => [ 'editsemiprotected', 'privileged' ],
1288 ],
1289 ] );
1290 $this->assertSame( $expected, $obj->getRestrictionLevels( $ns, $user ) );
1291 }
1292
1293 public function provideGetRestrictionLevels() {
1294 return [
1295 'No namespace restriction' => [ [ '', 'autoconfirmed', 'sysop' ], NS_TALK ],
1296 'Restricted to autoconfirmed' => [ [ '', 'sysop' ], NS_MAIN ],
1297 'Restricted to sysop' => [ [ '' ], NS_USER ],
1298 // @todo Bug -- 'sysop' protection should be allowed in this case. Someone who's
1299 // autoconfirmed and also privileged can edit this namespace, and would be blocked by
1300 // the sysop protection.
1301 'Restricted to someone in two groups' => [ [ '' ], 101 ],
1302
1303 'No special permissions' => [ [ '' ], NS_TALK, $this->getMockUser() ],
1304 'autoconfirmed' => [
1305 [ '', 'autoconfirmed' ],
1306 NS_TALK,
1307 $this->getMockUser( [ 'autoconfirmed' ] )
1308 ],
1309 'autoconfirmed revoked' => [
1310 [ '' ],
1311 NS_TALK,
1312 $this->getMockUser( [ 'autoconfirmed', 'noeditsemiprotected' ] )
1313 ],
1314 'sysop' => [
1315 [ '', 'autoconfirmed', 'sysop' ],
1316 NS_TALK,
1317 $this->getMockUser( [ 'sysop' ] )
1318 ],
1319 'sysop with autoconfirmed revoked (a bit silly)' => [
1320 [ '', 'sysop' ],
1321 NS_TALK,
1322 $this->getMockUser( [ 'sysop', 'noeditsemiprotected' ] )
1323 ],
1324 ];
1325 }
1326
1327 // %} End restriction levels
1328 }
1329
1330 /**
1331 * For really cool vim folding this needs to be at the end:
1332 * vim: foldmarker=%{,%} foldmethod=marker
1333 */