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