MediaWiki  1.33.0
MediaWikiTestCase.php
Go to the documentation of this file.
1 <?php
2 
8 use Psr\Log\LoggerInterface;
12 use Wikimedia\TestingAccessWrapper;
13 
17 abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
18 
19  use MediaWikiCoversValidator;
20  use PHPUnit4And6Compat;
21 
27  private static $originalServices;
28 
33  private $localServices;
34 
47  private $called = [];
48 
53  public static $users;
54 
61  protected $db;
62 
67  protected $tablesUsed = []; // tables with data
68 
69  private static $useTemporaryTables = true;
70  private static $reuseDB = false;
71  private static $dbSetup = false;
72  private static $oldTablePrefix = '';
73 
79  private $phpErrorLevel;
80 
87  private $tmpFiles = [];
88 
95  private $mwGlobals = [];
96 
102  private $mwGlobalsToUnset = [];
103 
110  private $iniSettings = [];
111 
116  private $loggers = [];
117 
122  private $cliArgs = [];
123 
129  private $overriddenServices = [];
130 
134  const DB_PREFIX = 'unittest_';
135  const ORA_DB_PREFIX = 'ut_';
136 
141  protected $supportedDBs = [
142  'mysql',
143  'sqlite',
144  'postgres',
145  'oracle'
146  ];
147 
148  public function __construct( $name = null, array $data = [], $dataName = '' ) {
149  parent::__construct( $name, $data, $dataName );
150 
151  $this->backupGlobals = false;
152  $this->backupStaticAttributes = false;
153  }
154 
155  public function __destruct() {
156  // Complain if self::setUp() was called, but not self::tearDown()
157  // $this->called['setUp'] will be checked by self::testMediaWikiTestCaseParentSetupCalled()
158  if ( isset( $this->called['setUp'] ) && !isset( $this->called['tearDown'] ) ) {
159  throw new MWException( static::class . "::tearDown() must call parent::tearDown()" );
160  }
161  }
162 
163  public static function setUpBeforeClass() {
164  parent::setUpBeforeClass();
165 
166  // Get the original service locator
167  if ( !self::$originalServices ) {
168  self::$originalServices = MediaWikiServices::getInstance();
169  }
170  }
171 
180  public static function getTestUser( $groups = [] ) {
181  return TestUserRegistry::getImmutableTestUser( $groups );
182  }
183 
192  public static function getMutableTestUser( $groups = [] ) {
193  return TestUserRegistry::getMutableTestUser( __CLASS__, $groups );
194  }
195 
204  public static function getTestSysop() {
205  return self::getTestUser( [ 'sysop', 'bureaucrat' ] );
206  }
207 
220  protected function getExistingTestPage( $title = null ) {
221  if ( !$this->needsDB() ) {
222  throw new MWException( 'When testing which pages, the test cases\'s needsDB()' .
223  ' method should return true. Use @group Database or $this->tablesUsed.' );
224  }
225 
226  $title = ( $title === null ) ? 'UTPage' : $title;
227  $title = is_string( $title ) ? Title::newFromText( $title ) : $title;
228  $page = WikiPage::factory( $title );
229 
230  if ( !$page->exists() ) {
231  $user = self::getTestSysop()->getUser();
232  $page->doEditContent(
233  new WikitextContent( 'UTContent' ),
234  'UTPageSummary',
236  false,
237  $user
238  );
239  }
240 
241  return $page;
242  }
243 
256  protected function getNonexistingTestPage( $title = null ) {
257  if ( !$this->needsDB() ) {
258  throw new MWException( 'When testing which pages, the test cases\'s needsDB()' .
259  ' method should return true. Use @group Database or $this->tablesUsed.' );
260  }
261 
262  $title = ( $title === null ) ? 'UTPage-' . rand( 0, 100000 ) : $title;
263  $title = is_string( $title ) ? Title::newFromText( $title ) : $title;
264  $page = WikiPage::factory( $title );
265 
266  if ( $page->exists() ) {
267  $page->doDeleteArticle( 'Testing' );
268  }
269 
270  return $page;
271  }
272 
276  public static function prepareServices( Config $bootstrapConfig ) {
277  }
278 
288  private static function makeTestConfig(
289  Config $baseConfig = null,
290  Config $customOverrides = null
291  ) {
292  $defaultOverrides = new HashConfig();
293 
294  if ( !$baseConfig ) {
295  $baseConfig = self::$originalServices->getBootstrapConfig();
296  }
297 
298  /* Some functions require some kind of caching, and will end up using the db,
299  * which we can't allow, as that would open a new connection for mysql.
300  * Replace with a HashBag. They would not be going to persist anyway.
301  */
302  $hashCache = [ 'class' => HashBagOStuff::class, 'reportDupes' => false ];
303  $objectCaches = [
304  CACHE_DB => $hashCache,
305  CACHE_ACCEL => $hashCache,
306  CACHE_MEMCACHED => $hashCache,
307  'apc' => $hashCache,
308  'apcu' => $hashCache,
309  'wincache' => $hashCache,
310  ] + $baseConfig->get( 'ObjectCaches' );
311 
312  $defaultOverrides->set( 'ObjectCaches', $objectCaches );
313  $defaultOverrides->set( 'MainCacheType', CACHE_NONE );
314  $defaultOverrides->set( 'JobTypeConf', [ 'default' => [ 'class' => JobQueueMemory::class ] ] );
315 
316  // Use a fast hash algorithm to hash passwords.
317  $defaultOverrides->set( 'PasswordDefault', 'A' );
318 
319  $testConfig = $customOverrides
320  ? new MultiConfig( [ $customOverrides, $defaultOverrides, $baseConfig ] )
321  : new MultiConfig( [ $defaultOverrides, $baseConfig ] );
322 
323  return $testConfig;
324  }
325 
332  private static function makeTestConfigFactoryInstantiator(
333  ConfigFactory $oldFactory,
334  array $configurations
335  ) {
336  return function ( MediaWikiServices $services ) use ( $oldFactory, $configurations ) {
337  $factory = new ConfigFactory();
338 
339  // clone configurations from $oldFactory that are not overwritten by $configurations
340  $namesToClone = array_diff(
341  $oldFactory->getConfigNames(),
342  array_keys( $configurations )
343  );
344 
345  foreach ( $namesToClone as $name ) {
346  $factory->register( $name, $oldFactory->makeConfig( $name ) );
347  }
348 
349  foreach ( $configurations as $name => $config ) {
350  $factory->register( $name, $config );
351  }
352 
353  return $factory;
354  };
355  }
356 
361  public static function resetNonServiceCaches() {
362  global $wgRequest, $wgJobClasses;
363 
365  foreach ( $wgJobClasses as $type => $class ) {
366  JobQueueGroup::singleton()->get( $type )->delete();
367  }
369 
373 
374  // TODO: move global state into MediaWikiServices
376  if ( session_id() !== '' ) {
377  session_write_close();
378  session_id( '' );
379  }
380 
381  $wgRequest = new FauxRequest();
383  }
384 
385  public function run( PHPUnit_Framework_TestResult $result = null ) {
386  if ( $result instanceof MediaWikiTestResult ) {
387  $this->cliArgs = $result->getMediaWikiCliArgs();
388  }
389  $this->overrideMwServices();
390 
391  if ( $this->needsDB() && !$this->isTestInDatabaseGroup() ) {
392  throw new Exception(
393  get_class( $this ) . ' apparently needsDB but is not in the Database group'
394  );
395  }
396 
397  $needsResetDB = false;
398  if ( !self::$dbSetup || $this->needsDB() ) {
399  // set up a DB connection for this test to use
400 
401  self::$useTemporaryTables = !$this->getCliArg( 'use-normal-tables' );
402  self::$reuseDB = $this->getCliArg( 'reuse-db' );
403 
404  $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
405  $this->db = $lb->getConnection( DB_MASTER );
406 
407  $this->checkDbIsSupported();
408 
409  if ( !self::$dbSetup ) {
410  $this->setupAllTestDBs();
411  $this->addCoreDBData();
412  }
413 
414  // TODO: the DB setup should be done in setUpBeforeClass(), so the test DB
415  // is available in subclass's setUpBeforeClass() and setUp() methods.
416  // This would also remove the need for the HACK that is oncePerClass().
417  if ( $this->oncePerClass() ) {
418  $this->setUpSchema( $this->db );
419  $this->resetDB( $this->db, $this->tablesUsed );
420  $this->addDBDataOnce();
421  }
422 
423  $this->addDBData();
424  $needsResetDB = true;
425  }
426 
427  parent::run( $result );
428 
429  // We don't mind if we override already-overridden services during cleanup
430  $this->overriddenServices = [];
431 
432  if ( $needsResetDB ) {
433  $this->resetDB( $this->db, $this->tablesUsed );
434  }
435 
437  $this->localServices = null;
438  }
439 
443  private function oncePerClass() {
444  // Remember current test class in the database connection,
445  // so we know when we need to run addData.
446 
447  $class = static::class;
448 
449  $first = !isset( $this->db->_hasDataForTestClass )
450  || $this->db->_hasDataForTestClass !== $class;
451 
452  $this->db->_hasDataForTestClass = $class;
453  return $first;
454  }
455 
461  public function usesTemporaryTables() {
463  }
464 
474  protected function getNewTempFile() {
475  $fileName = tempnam( wfTempDir(), 'MW_PHPUnit_' . static::class . '_' );
476  $this->tmpFiles[] = $fileName;
477 
478  return $fileName;
479  }
480 
491  protected function getNewTempDirectory() {
492  // Starting of with a temporary /file/.
493  $fileName = $this->getNewTempFile();
494 
495  // Converting the temporary /file/ to a /directory/
496  // The following is not atomic, but at least we now have a single place,
497  // where temporary directory creation is bundled and can be improved
498  unlink( $fileName );
499  $this->assertTrue( wfMkdirParents( $fileName ) );
500 
501  return $fileName;
502  }
503 
504  protected function setUp() {
505  parent::setUp();
506  $this->called['setUp'] = true;
507 
508  $this->phpErrorLevel = intval( ini_get( 'error_reporting' ) );
509 
510  $this->overriddenServices = [];
511 
512  // Cleaning up temporary files
513  foreach ( $this->tmpFiles as $fileName ) {
514  if ( is_file( $fileName ) || ( is_link( $fileName ) ) ) {
515  unlink( $fileName );
516  } elseif ( is_dir( $fileName ) ) {
517  wfRecursiveRemoveDir( $fileName );
518  }
519  }
520 
521  if ( $this->needsDB() && $this->db ) {
522  // Clean up open transactions
523  while ( $this->db->trxLevel() > 0 ) {
524  $this->db->rollback( __METHOD__, 'flush' );
525  }
526  // Check for unsafe queries
527  if ( $this->db->getType() === 'mysql' ) {
528  $this->db->query( "SET sql_mode = 'STRICT_ALL_TABLES'", __METHOD__ );
529  }
530  }
531 
532  // Reset all caches between tests.
534 
535  // XXX: reset maintenance triggers
536  // Hook into period lag checks which often happen in long-running scripts
537  $lbFactory = $this->localServices->getDBLoadBalancerFactory();
538  Maintenance::setLBFactoryTriggers( $lbFactory, $this->localServices->getMainConfig() );
539 
540  ob_start( 'MediaWikiTestCase::wfResetOutputBuffersBarrier' );
541  }
542 
543  protected function addTmpFiles( $files ) {
544  $this->tmpFiles = array_merge( $this->tmpFiles, (array)$files );
545  }
546 
547  protected function tearDown() {
548  global $wgRequest, $wgSQLMode;
549 
550  $status = ob_get_status();
551  if ( isset( $status['name'] ) &&
552  $status['name'] === 'MediaWikiTestCase::wfResetOutputBuffersBarrier'
553  ) {
554  ob_end_flush();
555  }
556 
557  $this->called['tearDown'] = true;
558  // Cleaning up temporary files
559  foreach ( $this->tmpFiles as $fileName ) {
560  if ( is_file( $fileName ) || ( is_link( $fileName ) ) ) {
561  unlink( $fileName );
562  } elseif ( is_dir( $fileName ) ) {
563  wfRecursiveRemoveDir( $fileName );
564  }
565  }
566 
567  if ( $this->needsDB() && $this->db ) {
568  // Clean up open transactions
569  while ( $this->db->trxLevel() > 0 ) {
570  $this->db->rollback( __METHOD__, 'flush' );
571  }
572  if ( $this->db->getType() === 'mysql' ) {
573  $this->db->query( "SET sql_mode = " . $this->db->addQuotes( $wgSQLMode ),
574  __METHOD__ );
575  }
576  }
577 
578  // Re-enable any disabled deprecation warnings
580  // Restore mw globals
581  foreach ( $this->mwGlobals as $key => $value ) {
582  $GLOBALS[$key] = $value;
583  }
584  foreach ( $this->mwGlobalsToUnset as $value ) {
585  unset( $GLOBALS[$value] );
586  }
587  foreach ( $this->iniSettings as $name => $value ) {
588  ini_set( $name, $value );
589  }
590  if (
591  array_key_exists( 'wgExtraNamespaces', $this->mwGlobals ) ||
592  in_array( 'wgExtraNamespaces', $this->mwGlobalsToUnset )
593  ) {
594  $this->resetNamespaces();
595  }
596  $this->mwGlobals = [];
597  $this->mwGlobalsToUnset = [];
598  $this->restoreLoggers();
599 
600  // TODO: move global state into MediaWikiServices
602  if ( session_id() !== '' ) {
603  session_write_close();
604  session_id( '' );
605  }
606  $wgRequest = new FauxRequest();
609 
610  $phpErrorLevel = intval( ini_get( 'error_reporting' ) );
611 
612  if ( $phpErrorLevel !== $this->phpErrorLevel ) {
613  ini_set( 'error_reporting', $this->phpErrorLevel );
614 
615  $oldHex = strtoupper( dechex( $this->phpErrorLevel ) );
616  $newHex = strtoupper( dechex( $phpErrorLevel ) );
617  $message = "PHP error_reporting setting was left dirty: "
618  . "was 0x$oldHex before test, 0x$newHex after test!";
619 
620  $this->fail( $message );
621  }
622 
623  parent::tearDown();
624  }
625 
634  final public function testMediaWikiTestCaseParentSetupCalled() {
635  $this->assertArrayHasKey( 'setUp', $this->called,
636  static::class . '::setUp() must call parent::setUp()'
637  );
638  }
639 
649  protected function setService( $name, $object ) {
650  if ( !$this->localServices ) {
651  throw new Exception( __METHOD__ . ' must be called after MediaWikiTestCase::run()' );
652  }
653 
654  if ( $this->localServices !== MediaWikiServices::getInstance() ) {
655  throw new Exception( __METHOD__ . ' will not work because the global MediaWikiServices '
656  . 'instance has been replaced by test code.' );
657  }
658 
659  $this->overriddenServices[] = $name;
660 
661  $this->localServices->disableService( $name );
662  $this->localServices->redefineService(
663  $name,
664  function () use ( $object ) {
665  return $object;
666  }
667  );
668 
669  if ( $name === 'ContentLanguage' ) {
670  $this->doSetMwGlobals( [ 'wgContLang' => $object ] );
671  }
672  }
673 
709  protected function setMwGlobals( $pairs, $value = null ) {
710  if ( is_string( $pairs ) ) {
711  $pairs = [ $pairs => $value ];
712  }
713 
714  if ( isset( $pairs['wgContLang'] ) ) {
715  throw new MWException(
716  'No setting $wgContLang, use setContentLang() or setService( \'ContentLanguage\' )'
717  );
718  }
719 
720  $this->doSetMwGlobals( $pairs, $value );
721  }
722 
727  private function doSetMwGlobals( $pairs, $value = null ) {
728  $this->doStashMwGlobals( array_keys( $pairs ) );
729 
730  foreach ( $pairs as $key => $value ) {
731  $GLOBALS[$key] = $value;
732  }
733 
734  if ( array_key_exists( 'wgExtraNamespaces', $pairs ) ) {
735  $this->resetNamespaces();
736  }
737  }
738 
745  protected function setIniSetting( $name, $value ) {
746  $original = ini_get( $name );
747  $this->iniSettings[$name] = $original;
748  ini_set( $name, $value );
749  }
750 
755  private function resetNamespaces() {
756  if ( !$this->localServices ) {
757  throw new Exception( __METHOD__ . ' must be called after MediaWikiTestCase::run()' );
758  }
759 
760  if ( $this->localServices !== MediaWikiServices::getInstance() ) {
761  throw new Exception( __METHOD__ . ' will not work because the global MediaWikiServices '
762  . 'instance has been replaced by test code.' );
763  }
764 
767 
768  // We can't have the TitleFormatter holding on to an old Language object either
769  // @todo We shouldn't need to reset all the aliases here.
770  $this->localServices->resetServiceForTesting( 'TitleFormatter' );
771  $this->localServices->resetServiceForTesting( 'TitleParser' );
772  $this->localServices->resetServiceForTesting( '_MediaWikiTitleCodec' );
773  }
774 
783  private static function canShallowCopy( $value ) {
784  if ( is_scalar( $value ) || $value === null ) {
785  return true;
786  }
787  if ( is_array( $value ) ) {
788  foreach ( $value as $subValue ) {
789  if ( !is_scalar( $subValue ) && $subValue !== null ) {
790  return false;
791  }
792  }
793  return true;
794  }
795  return false;
796  }
797 
816  protected function stashMwGlobals( $globalKeys ) {
817  wfDeprecated( __METHOD__, '1.32' );
818  $this->doStashMwGlobals( $globalKeys );
819  }
820 
821  private function doStashMwGlobals( $globalKeys ) {
822  if ( is_string( $globalKeys ) ) {
823  $globalKeys = [ $globalKeys ];
824  }
825 
826  foreach ( $globalKeys as $globalKey ) {
827  // NOTE: make sure we only save the global once or a second call to
828  // setMwGlobals() on the same global would override the original
829  // value.
830  if (
831  !array_key_exists( $globalKey, $this->mwGlobals ) &&
832  !array_key_exists( $globalKey, $this->mwGlobalsToUnset )
833  ) {
834  if ( !array_key_exists( $globalKey, $GLOBALS ) ) {
835  $this->mwGlobalsToUnset[$globalKey] = $globalKey;
836  continue;
837  }
838  // NOTE: we serialize then unserialize the value in case it is an object
839  // this stops any objects being passed by reference. We could use clone
840  // and if is_object but this does account for objects within objects!
841  if ( self::canShallowCopy( $GLOBALS[$globalKey] ) ) {
842  $this->mwGlobals[$globalKey] = $GLOBALS[$globalKey];
843  } elseif (
844  // Many MediaWiki types are safe to clone. These are the
845  // ones that are most commonly stashed.
846  $GLOBALS[$globalKey] instanceof Language ||
847  $GLOBALS[$globalKey] instanceof User ||
848  $GLOBALS[$globalKey] instanceof FauxRequest
849  ) {
850  $this->mwGlobals[$globalKey] = clone $GLOBALS[$globalKey];
851  } elseif ( $this->containsClosure( $GLOBALS[$globalKey] ) ) {
852  // Serializing Closure only gives a warning on HHVM while
853  // it throws an Exception on Zend.
854  // Workaround for https://github.com/facebook/hhvm/issues/6206
855  $this->mwGlobals[$globalKey] = $GLOBALS[$globalKey];
856  } else {
857  try {
858  $this->mwGlobals[$globalKey] = unserialize( serialize( $GLOBALS[$globalKey] ) );
859  } catch ( Exception $e ) {
860  $this->mwGlobals[$globalKey] = $GLOBALS[$globalKey];
861  }
862  }
863  }
864  }
865  }
866 
873  private function containsClosure( $var, $maxDepth = 15 ) {
874  if ( $var instanceof Closure ) {
875  return true;
876  }
877  if ( !is_array( $var ) || $maxDepth === 0 ) {
878  return false;
879  }
880 
881  foreach ( $var as $value ) {
882  if ( $this->containsClosure( $value, $maxDepth - 1 ) ) {
883  return true;
884  }
885  }
886  return false;
887  }
888 
904  protected function mergeMwGlobalArrayValue( $name, $values ) {
905  if ( !isset( $GLOBALS[$name] ) ) {
906  $merged = $values;
907  } else {
908  if ( !is_array( $GLOBALS[$name] ) ) {
909  throw new MWException( "MW global $name is not an array." );
910  }
911 
912  // NOTE: do not use array_merge, it screws up for numeric keys.
913  $merged = $GLOBALS[$name];
914  foreach ( $values as $k => $v ) {
915  $merged[$k] = $v;
916  }
917  }
918 
919  $this->setMwGlobals( $name, $merged );
920  }
921 
937  protected function overrideMwServices(
938  Config $configOverrides = null, array $services = []
939  ) {
940  if ( $this->overriddenServices ) {
941  throw new MWException(
942  'The following services were set and are now being unset by overrideMwServices: ' .
943  implode( ', ', $this->overriddenServices )
944  );
945  }
946  $newInstance = self::installMockMwServices( $configOverrides );
947 
948  if ( $this->localServices ) {
949  $this->localServices->destroy();
950  }
951 
952  $this->localServices = $newInstance;
953 
954  foreach ( $services as $name => $callback ) {
955  $newInstance->redefineService( $name, $callback );
956  }
957 
958  return $newInstance;
959  }
960 
978  public static function installMockMwServices( Config $configOverrides = null ) {
979  // Make sure we have the original service locator
980  if ( !self::$originalServices ) {
981  self::$originalServices = MediaWikiServices::getInstance();
982  }
983 
984  if ( !$configOverrides ) {
985  $configOverrides = new HashConfig();
986  }
987 
988  $oldConfigFactory = self::$originalServices->getConfigFactory();
989  $oldLoadBalancerFactory = self::$originalServices->getDBLoadBalancerFactory();
990 
991  $testConfig = self::makeTestConfig( null, $configOverrides );
992  $newServices = new MediaWikiServices( $testConfig );
993 
994  // Load the default wiring from the specified files.
995  // NOTE: this logic mirrors the logic in MediaWikiServices::newInstance.
996  $wiringFiles = $testConfig->get( 'ServiceWiringFiles' );
997  $newServices->loadWiringFiles( $wiringFiles );
998 
999  // Provide a traditional hook point to allow extensions to configure services.
1000  Hooks::run( 'MediaWikiServices', [ $newServices ] );
1001 
1002  // Use bootstrap config for all configuration.
1003  // This allows config overrides via global variables to take effect.
1004  $bootstrapConfig = $newServices->getBootstrapConfig();
1005  $newServices->resetServiceForTesting( 'ConfigFactory' );
1006  $newServices->redefineService(
1007  'ConfigFactory',
1008  self::makeTestConfigFactoryInstantiator(
1009  $oldConfigFactory,
1010  [ 'main' => $bootstrapConfig ]
1011  )
1012  );
1013  $newServices->resetServiceForTesting( 'DBLoadBalancerFactory' );
1014  $newServices->redefineService(
1015  'DBLoadBalancerFactory',
1016  function ( MediaWikiServices $services ) use ( $oldLoadBalancerFactory ) {
1017  return $oldLoadBalancerFactory;
1018  }
1019  );
1020 
1021  MediaWikiServices::forceGlobalInstance( $newServices );
1022  return $newServices;
1023  }
1024 
1036  public static function restoreMwServices() {
1037  if ( !self::$originalServices ) {
1038  return false;
1039  }
1040 
1041  $currentServices = MediaWikiServices::getInstance();
1042 
1043  if ( self::$originalServices === $currentServices ) {
1044  return false;
1045  }
1046 
1047  MediaWikiServices::forceGlobalInstance( self::$originalServices );
1048  $currentServices->destroy();
1049 
1050  return true;
1051  }
1052 
1057  public function setUserLang( $lang ) {
1058  RequestContext::getMain()->setLanguage( $lang );
1059  $this->setMwGlobals( 'wgLang', RequestContext::getMain()->getLanguage() );
1060  }
1061 
1066  public function setContentLang( $lang ) {
1067  if ( $lang instanceof Language ) {
1068  $this->setMwGlobals( 'wgLanguageCode', $lang->getCode() );
1069  // Set to the exact object requested
1070  $this->setService( 'ContentLanguage', $lang );
1071  } else {
1072  $this->setMwGlobals( 'wgLanguageCode', $lang );
1073  // Let the service handler make up the object. Avoid calling setService(), because if
1074  // we do, overrideMwServices() will complain if it's called later on.
1075  $services = MediaWikiServices::getInstance();
1076  $services->resetServiceForTesting( 'ContentLanguage' );
1077  $this->doSetMwGlobals( [ 'wgContLang' => $services->getContentLanguage() ] );
1078  }
1079  }
1080 
1095  public function setGroupPermissions( $newPerms, $newKey = null, $newValue = null ) {
1096  global $wgGroupPermissions;
1097 
1098  if ( is_string( $newPerms ) ) {
1099  $newPerms = [ $newPerms => [ $newKey => $newValue ] ];
1100  }
1101 
1102  $newPermissions = $wgGroupPermissions;
1103  foreach ( $newPerms as $group => $permissions ) {
1104  foreach ( $permissions as $key => $value ) {
1105  $newPermissions[$group][$key] = $value;
1106  }
1107  }
1108 
1109  $this->setMwGlobals( 'wgGroupPermissions', $newPermissions );
1110  }
1111 
1118  protected function setLogger( $channel, LoggerInterface $logger ) {
1119  // TODO: Once loggers are managed by MediaWikiServices, use
1120  // overrideMwServices() to set loggers.
1121 
1122  $provider = LoggerFactory::getProvider();
1123  $wrappedProvider = TestingAccessWrapper::newFromObject( $provider );
1124  $singletons = $wrappedProvider->singletons;
1125  if ( $provider instanceof MonologSpi ) {
1126  if ( !isset( $this->loggers[$channel] ) ) {
1127  $this->loggers[$channel] = $singletons['loggers'][$channel] ?? null;
1128  }
1129  $singletons['loggers'][$channel] = $logger;
1130  } elseif ( $provider instanceof LegacySpi || $provider instanceof LogCapturingSpi ) {
1131  if ( !isset( $this->loggers[$channel] ) ) {
1132  $this->loggers[$channel] = $singletons[$channel] ?? null;
1133  }
1134  $singletons[$channel] = $logger;
1135  } else {
1136  throw new LogicException( __METHOD__ . ': setting a logger for ' . get_class( $provider )
1137  . ' is not implemented' );
1138  }
1139  $wrappedProvider->singletons = $singletons;
1140  }
1141 
1146  private function restoreLoggers() {
1147  $provider = LoggerFactory::getProvider();
1148  $wrappedProvider = TestingAccessWrapper::newFromObject( $provider );
1149  $singletons = $wrappedProvider->singletons;
1150  foreach ( $this->loggers as $channel => $logger ) {
1151  if ( $provider instanceof MonologSpi ) {
1152  if ( $logger === null ) {
1153  unset( $singletons['loggers'][$channel] );
1154  } else {
1155  $singletons['loggers'][$channel] = $logger;
1156  }
1157  } elseif ( $provider instanceof LegacySpi || $provider instanceof LogCapturingSpi ) {
1158  if ( $logger === null ) {
1159  unset( $singletons[$channel] );
1160  } else {
1161  $singletons[$channel] = $logger;
1162  }
1163  }
1164  }
1165  $wrappedProvider->singletons = $singletons;
1166  $this->loggers = [];
1167  }
1168 
1173  public function dbPrefix() {
1174  return self::getTestPrefixFor( $this->db );
1175  }
1176 
1182  public static function getTestPrefixFor( IDatabase $db ) {
1183  return $db->getType() == 'oracle' ? self::ORA_DB_PREFIX : self::DB_PREFIX;
1184  }
1185 
1190  public function needsDB() {
1191  // If the test says it uses database tables, it needs the database
1192  return $this->tablesUsed || $this->isTestInDatabaseGroup();
1193  }
1194 
1199  protected function isTestInDatabaseGroup() {
1200  // If the test class says it belongs to the Database group, it needs the database.
1201  // NOTE: This ONLY checks for the group in the class level doc comment.
1202  $rc = new ReflectionClass( $this );
1203  return (bool)preg_match( '/@group +Database/im', $rc->getDocComment() );
1204  }
1205 
1222  protected function insertPage(
1223  $pageName,
1224  $text = 'Sample page for unit test.',
1225  $namespace = null,
1226  User $user = null
1227  ) {
1228  if ( !$this->needsDB() ) {
1229  throw new MWException( 'When testing which pages, the test cases\'s needsDB()' .
1230  ' method should return true. Use @group Database or $this->tablesUsed.' );
1231  }
1232 
1233  if ( is_string( $pageName ) ) {
1234  $title = Title::newFromText( $pageName, $namespace );
1235  } else {
1236  $title = $pageName;
1237  }
1238 
1239  if ( !$user ) {
1240  $user = static::getTestSysop()->getUser();
1241  }
1242  $comment = __METHOD__ . ': Sample page for unit test.';
1243 
1244  $page = WikiPage::factory( $title );
1245  $page->doEditContent( ContentHandler::makeContent( $text, $title ), $comment, 0, false, $user );
1246 
1247  return [
1248  'title' => $title,
1249  'id' => $page->getId(),
1250  ];
1251  }
1252 
1268  public function addDBDataOnce() {
1269  }
1270 
1280  public function addDBData() {
1281  }
1282 
1286  protected function addCoreDBData() {
1287  if ( $this->db->getType() == 'oracle' ) {
1288  # Insert 0 user to prevent FK violations
1289  # Anonymous user
1290  if ( !$this->db->selectField( 'user', '1', [ 'user_id' => 0 ] ) ) {
1291  $this->db->insert( 'user', [
1292  'user_id' => 0,
1293  'user_name' => 'Anonymous' ], __METHOD__, [ 'IGNORE' ] );
1294  }
1295 
1296  # Insert 0 page to prevent FK violations
1297  # Blank page
1298  if ( !$this->db->selectField( 'page', '1', [ 'page_id' => 0 ] ) ) {
1299  $this->db->insert( 'page', [
1300  'page_id' => 0,
1301  'page_namespace' => 0,
1302  'page_title' => ' ',
1303  'page_restrictions' => null,
1304  'page_is_redirect' => 0,
1305  'page_is_new' => 0,
1306  'page_random' => 0,
1307  'page_touched' => $this->db->timestamp(),
1308  'page_latest' => 0,
1309  'page_len' => 0 ], __METHOD__, [ 'IGNORE' ] );
1310  }
1311  }
1312 
1314 
1316 
1317  // Make sysop user
1318  $user = static::getTestSysop()->getUser();
1319 
1320  // Make 1 page with 1 revision
1321  $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
1322  if ( $page->getId() == 0 ) {
1323  $page->doEditContent(
1324  new WikitextContent( 'UTContent' ),
1325  'UTPageSummary',
1327  false,
1328  $user
1329  );
1330  // an edit always attempt to purge backlink links such as history
1331  // pages. That is unnecessary.
1332  JobQueueGroup::singleton()->get( 'htmlCacheUpdate' )->delete();
1333  // WikiPages::doEditUpdates randomly adds RC purges
1334  JobQueueGroup::singleton()->get( 'recentChangesUpdate' )->delete();
1335 
1336  // doEditContent() probably started the session via
1337  // User::loadFromSession(). Close it now.
1338  if ( session_id() !== '' ) {
1339  session_write_close();
1340  session_id( '' );
1341  }
1342  }
1343  }
1344 
1354  public static function teardownTestDB() {
1355  global $wgJobClasses;
1356 
1357  if ( !self::$dbSetup ) {
1358  return;
1359  }
1360 
1361  Hooks::run( 'UnitTestsBeforeDatabaseTeardown' );
1362 
1363  foreach ( $wgJobClasses as $type => $class ) {
1364  // Delete any jobs under the clone DB (or old prefix in other stores)
1365  JobQueueGroup::singleton()->get( $type )->delete();
1366  }
1367 
1368  // T219673: close any connections from code that failed to call reuseConnection()
1369  // or is still holding onto a DBConnRef instance (e.g. in a singleton).
1370  MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->closeAll();
1371  CloneDatabase::changePrefix( self::$oldTablePrefix );
1372 
1373  self::$oldTablePrefix = false;
1374  self::$dbSetup = false;
1375  }
1376 
1388  protected static function setupDatabaseWithTestPrefix(
1390  $prefix = null
1391  ) {
1392  if ( $prefix === null ) {
1393  $prefix = self::getTestPrefixFor( $db );
1394  }
1395 
1396  if ( ( $db->getType() == 'oracle' || !self::$useTemporaryTables ) && self::$reuseDB ) {
1397  $db->tablePrefix( $prefix );
1398  return false;
1399  }
1400 
1401  if ( !isset( $db->_originalTablePrefix ) ) {
1402  $oldPrefix = $db->tablePrefix();
1403 
1404  if ( $oldPrefix === $prefix ) {
1405  // table already has the correct prefix, but presumably no cloned tables
1406  $oldPrefix = self::$oldTablePrefix;
1407  }
1408 
1409  $db->tablePrefix( $oldPrefix );
1410  $tablesCloned = self::listTables( $db );
1411  $dbClone = new CloneDatabase( $db, $tablesCloned, $prefix, $oldPrefix );
1412  $dbClone->useTemporaryTables( self::$useTemporaryTables );
1413 
1414  $dbClone->cloneTableStructure();
1415 
1416  $db->tablePrefix( $prefix );
1417  $db->_originalTablePrefix = $oldPrefix;
1418  }
1419 
1420  return true;
1421  }
1422 
1426  public function setupAllTestDBs() {
1427  global $wgDBprefix;
1428 
1429  self::$oldTablePrefix = $wgDBprefix;
1430 
1431  $testPrefix = $this->dbPrefix();
1432 
1433  // switch to a temporary clone of the database
1434  self::setupTestDB( $this->db, $testPrefix );
1435 
1436  if ( self::isUsingExternalStoreDB() ) {
1437  self::setupExternalStoreTestDBs( $testPrefix );
1438  }
1439 
1440  // NOTE: Change the prefix in the LBFactory and $wgDBprefix, to prevent
1441  // *any* database connections to operate on live data.
1442  CloneDatabase::changePrefix( $testPrefix );
1443  }
1444 
1466  public static function setupTestDB( IMaintainableDatabase $db, $prefix ) {
1467  if ( self::$dbSetup ) {
1468  return;
1469  }
1470 
1471  if ( $db->tablePrefix() === $prefix ) {
1472  throw new MWException(
1473  'Cannot run unit tests, the database prefix is already "' . $prefix . '"' );
1474  }
1475 
1476  // TODO: the below should be re-written as soon as LBFactory, LoadBalancer,
1477  // and Database no longer use global state.
1478 
1479  self::$dbSetup = true;
1480 
1481  if ( !self::setupDatabaseWithTestPrefix( $db, $prefix ) ) {
1482  return;
1483  }
1484 
1485  // Assuming this isn't needed for External Store database, and not sure if the procedure
1486  // would be available there.
1487  if ( $db->getType() == 'oracle' ) {
1488  $db->query( 'BEGIN FILL_WIKI_INFO; END;', __METHOD__ );
1489  }
1490 
1491  Hooks::run( 'UnitTestsAfterDatabaseSetup', [ $db, $prefix ] );
1492  }
1493 
1500  protected static function setupExternalStoreTestDBs( $testPrefix = null ) {
1502  foreach ( $connections as $dbw ) {
1503  self::setupDatabaseWithTestPrefix( $dbw, $testPrefix );
1504  }
1505  }
1506 
1513  protected static function getExternalStoreDatabaseConnections() {
1514  global $wgDefaultExternalStore;
1515 
1517  $externalStoreDB = ExternalStore::getStoreObject( 'DB' );
1518  $defaultArray = (array)$wgDefaultExternalStore;
1519  $dbws = [];
1520  foreach ( $defaultArray as $url ) {
1521  if ( strpos( $url, 'DB://' ) === 0 ) {
1522  list( $proto, $cluster ) = explode( '://', $url, 2 );
1523  // Avoid getMaster() because setupDatabaseWithTestPrefix()
1524  // requires Database instead of plain DBConnRef/IDatabase
1525  $dbws[] = $externalStoreDB->getMaster( $cluster );
1526  }
1527  }
1528 
1529  return $dbws;
1530  }
1531 
1537  protected static function isUsingExternalStoreDB() {
1538  global $wgDefaultExternalStore;
1539  if ( !$wgDefaultExternalStore ) {
1540  return false;
1541  }
1542 
1543  $defaultArray = (array)$wgDefaultExternalStore;
1544  foreach ( $defaultArray as $url ) {
1545  if ( strpos( $url, 'DB://' ) === 0 ) {
1546  return true;
1547  }
1548  }
1549 
1550  return false;
1551  }
1552 
1560  if ( $db->tablePrefix() !== $this->dbPrefix() ) {
1561  throw new LogicException(
1562  'Trying to delete mock tables, but table prefix does not indicate a mock database.'
1563  );
1564  }
1565  }
1566 
1567  private static $schemaOverrideDefaults = [
1568  'scripts' => [],
1569  'create' => [],
1570  'drop' => [],
1571  'alter' => [],
1572  ];
1573 
1589  return [];
1590  }
1591 
1599  private function undoSchemaOverrides( IMaintainableDatabase $db, $oldOverrides ) {
1600  $this->ensureMockDatabaseConnection( $db );
1601 
1602  $oldOverrides = $oldOverrides + self::$schemaOverrideDefaults;
1603  $originalTables = $this->listOriginalTables( $db );
1604 
1605  // Drop tables that need to be restored or removed.
1606  $tablesToDrop = array_merge( $oldOverrides['create'], $oldOverrides['alter'] );
1607 
1608  // Restore tables that have been dropped or created or altered,
1609  // if they exist in the original schema.
1610  $tablesToRestore = array_merge( $tablesToDrop, $oldOverrides['drop'] );
1611  $tablesToRestore = array_intersect( $originalTables, $tablesToRestore );
1612 
1613  if ( $tablesToDrop ) {
1614  $this->dropMockTables( $db, $tablesToDrop );
1615  }
1616 
1617  if ( $tablesToRestore ) {
1618  $this->recloneMockTables( $db, $tablesToRestore );
1619 
1620  // Reset the restored tables, mainly for the side effect of
1621  // re-calling $this->addCoreDBData() if necessary.
1622  $this->resetDB( $db, $tablesToRestore );
1623  }
1624  }
1625 
1631  private function setUpSchema( IMaintainableDatabase $db ) {
1632  // Undo any active overrides.
1633  $oldOverrides = $db->_schemaOverrides ?? self::$schemaOverrideDefaults;
1634 
1635  if ( $oldOverrides['alter'] || $oldOverrides['create'] || $oldOverrides['drop'] ) {
1636  $this->undoSchemaOverrides( $db, $oldOverrides );
1637  unset( $db->_schemaOverrides );
1638  }
1639 
1640  // Determine new overrides.
1641  $overrides = $this->getSchemaOverrides( $db ) + self::$schemaOverrideDefaults;
1642 
1643  $extraKeys = array_diff(
1644  array_keys( $overrides ),
1645  array_keys( self::$schemaOverrideDefaults )
1646  );
1647 
1648  if ( $extraKeys ) {
1649  throw new InvalidArgumentException(
1650  'Schema override contains extra keys: ' . var_export( $extraKeys, true )
1651  );
1652  }
1653 
1654  if ( !$overrides['scripts'] ) {
1655  // no scripts to run
1656  return;
1657  }
1658 
1659  if ( !$overrides['create'] && !$overrides['drop'] && !$overrides['alter'] ) {
1660  throw new InvalidArgumentException(
1661  'Schema override scripts given, but no tables are declared to be '
1662  . 'created, dropped or altered.'
1663  );
1664  }
1665 
1666  $this->ensureMockDatabaseConnection( $db );
1667 
1668  // Drop the tables that will be created by the schema scripts.
1669  $originalTables = $this->listOriginalTables( $db );
1670  $tablesToDrop = array_intersect( $originalTables, $overrides['create'] );
1671 
1672  if ( $tablesToDrop ) {
1673  $this->dropMockTables( $db, $tablesToDrop );
1674  }
1675 
1676  // Run schema override scripts.
1677  foreach ( $overrides['scripts'] as $script ) {
1678  $db->sourceFile(
1679  $script,
1680  null,
1681  null,
1682  __METHOD__,
1683  function ( $cmd ) {
1684  return $this->mungeSchemaUpdateQuery( $cmd );
1685  }
1686  );
1687  }
1688 
1689  $db->_schemaOverrides = $overrides;
1690  }
1691 
1692  private function mungeSchemaUpdateQuery( $cmd ) {
1693  return self::$useTemporaryTables
1694  ? preg_replace( '/\bCREATE\s+TABLE\b/i', 'CREATE TEMPORARY TABLE', $cmd )
1695  : $cmd;
1696  }
1697 
1705  $this->ensureMockDatabaseConnection( $db );
1706 
1707  foreach ( $tables as $tbl ) {
1708  $tbl = $db->tableName( $tbl );
1709  $db->query( "DROP TABLE IF EXISTS $tbl", __METHOD__ );
1710  }
1711  }
1712 
1720  if ( !isset( $db->_originalTablePrefix ) ) {
1721  throw new LogicException( 'No original table prefix know, cannot list tables!' );
1722  }
1723 
1724  $originalTables = $db->listTables( $db->_originalTablePrefix, __METHOD__ );
1725 
1726  $unittestPrefixRegex = '/^' . preg_quote( $this->dbPrefix(), '/' ) . '/';
1727  $originalPrefixRegex = '/^' . preg_quote( $db->_originalTablePrefix, '/' ) . '/';
1728 
1729  $originalTables = array_filter(
1730  $originalTables,
1731  function ( $pt ) use ( $unittestPrefixRegex ) {
1732  return !preg_match( $unittestPrefixRegex, $pt );
1733  }
1734  );
1735 
1736  $originalTables = array_map(
1737  function ( $pt ) use ( $originalPrefixRegex ) {
1738  return preg_replace( $originalPrefixRegex, '', $pt );
1739  },
1740  $originalTables
1741  );
1742 
1743  return array_unique( $originalTables );
1744  }
1745 
1755  $this->ensureMockDatabaseConnection( $db );
1756 
1757  if ( !isset( $db->_originalTablePrefix ) ) {
1758  throw new LogicException( 'No original table prefix know, cannot restore tables!' );
1759  }
1760 
1761  $originalTables = $this->listOriginalTables( $db );
1762  $tables = array_intersect( $tables, $originalTables );
1763 
1764  $dbClone = new CloneDatabase( $db, $tables, $db->tablePrefix(), $db->_originalTablePrefix );
1765  $dbClone->useTemporaryTables( self::$useTemporaryTables );
1766 
1767  $dbClone->cloneTableStructure();
1768  }
1769 
1776  private function resetDB( $db, $tablesUsed ) {
1777  if ( $db ) {
1778  $userTables = [ 'user', 'user_groups', 'user_properties', 'actor' ];
1779  $pageTables = [
1780  'page', 'revision', 'ip_changes', 'revision_comment_temp', 'comment', 'archive',
1781  'revision_actor_temp', 'slots', 'content', 'content_models', 'slot_roles',
1782  ];
1783  $coreDBDataTables = array_merge( $userTables, $pageTables );
1784 
1785  // If any of the user or page tables were marked as used, we should clear all of them.
1786  if ( array_intersect( $tablesUsed, $userTables ) ) {
1787  $tablesUsed = array_unique( array_merge( $tablesUsed, $userTables ) );
1789 
1790  // Reset $wgUser, which is probably 127.0.0.1, as its loaded data is probably not valid
1791  // @todo Should we start setting $wgUser to something nondeterministic
1792  // to encourage tests to be updated to not depend on it?
1793  global $wgUser;
1794  $wgUser->clearInstanceCache( $wgUser->mFrom );
1795  }
1796  if ( array_intersect( $tablesUsed, $pageTables ) ) {
1797  $tablesUsed = array_unique( array_merge( $tablesUsed, $pageTables ) );
1798  }
1799 
1800  // Postgres, Oracle, and MSSQL all use mwuser/pagecontent
1801  // instead of user/text. But Postgres does not remap the
1802  // table name in tableExists(), so we mark the real table
1803  // names as being used.
1804  if ( $db->getType() === 'postgres' ) {
1805  if ( in_array( 'user', $tablesUsed ) ) {
1806  $tablesUsed[] = 'mwuser';
1807  }
1808  if ( in_array( 'text', $tablesUsed ) ) {
1809  $tablesUsed[] = 'pagecontent';
1810  }
1811  }
1812 
1813  foreach ( $tablesUsed as $tbl ) {
1814  $this->truncateTable( $tbl, $db );
1815  }
1816 
1817  if ( array_intersect( $tablesUsed, $coreDBDataTables ) ) {
1818  // Reset services that may contain information relating to the truncated tables
1819  $this->overrideMwServices();
1820  // Re-add core DB data that was deleted
1821  $this->addCoreDBData();
1822  }
1823  }
1824  }
1825 
1834  protected function truncateTable( $tableName, IDatabase $db = null ) {
1835  if ( !$db ) {
1836  $db = $this->db;
1837  }
1838 
1839  if ( !$db->tableExists( $tableName ) ) {
1840  return;
1841  }
1842 
1843  $truncate = in_array( $db->getType(), [ 'oracle', 'mysql' ] );
1844 
1845  if ( $truncate ) {
1846  $db->query( 'TRUNCATE TABLE ' . $db->tableName( $tableName ), __METHOD__ );
1847  } else {
1848  $db->delete( $tableName, '*', __METHOD__ );
1849  }
1850 
1851  if ( $db instanceof DatabasePostgres || $db instanceof DatabaseSqlite ) {
1852  // Reset the table's sequence too.
1853  $db->resetSequenceForTable( $tableName, __METHOD__ );
1854  }
1855 
1856  // re-initialize site_stats table
1857  if ( $tableName === 'site_stats' ) {
1859  }
1860  }
1861 
1862  private static function unprefixTable( &$tableName, $ind, $prefix ) {
1863  $tableName = substr( $tableName, strlen( $prefix ) );
1864  }
1865 
1866  private static function isNotUnittest( $table ) {
1867  return strpos( $table, self::DB_PREFIX ) !== 0;
1868  }
1869 
1877  public static function listTables( IMaintainableDatabase $db ) {
1878  $prefix = $db->tablePrefix();
1879  $tables = $db->listTables( $prefix, __METHOD__ );
1880 
1881  if ( $db->getType() === 'mysql' ) {
1882  static $viewListCache = null;
1883  if ( $viewListCache === null ) {
1884  $viewListCache = $db->listViews( null, __METHOD__ );
1885  }
1886  // T45571: cannot clone VIEWs under MySQL
1887  $tables = array_diff( $tables, $viewListCache );
1888  }
1889  array_walk( $tables, [ __CLASS__, 'unprefixTable' ], $prefix );
1890 
1891  // Don't duplicate test tables from the previous fataled run
1892  $tables = array_filter( $tables, [ __CLASS__, 'isNotUnittest' ] );
1893 
1894  if ( $db->getType() == 'sqlite' ) {
1895  $tables = array_flip( $tables );
1896  // these are subtables of searchindex and don't need to be duped/dropped separately
1897  unset( $tables['searchindex_content'] );
1898  unset( $tables['searchindex_segdir'] );
1899  unset( $tables['searchindex_segments'] );
1900  $tables = array_flip( $tables );
1901  }
1902 
1903  return $tables;
1904  }
1905 
1914  public function copyTestData( IDatabase $source, IDatabase $target ) {
1915  if ( $this->db->getType() === 'sqlite' ) {
1916  // SQLite uses a non-temporary copy of the searchindex table for testing,
1917  // which gets deleted and re-created when setting up the secondary connection,
1918  // causing "Error 17" when trying to copy the data. See T191863#4130112.
1919  throw new RuntimeException(
1920  'Setting up a secondary database connection with test data is currently not'
1921  . 'with SQLite. You may want to use markTestSkippedIfDbType() to bypass this issue.'
1922  );
1923  }
1924 
1926 
1927  foreach ( $tables as $table ) {
1928  $res = $source->select( $table, '*', [], __METHOD__ );
1929  $allRows = [];
1930 
1931  foreach ( $res as $row ) {
1932  $allRows[] = (array)$row;
1933  }
1934 
1935  $target->insert( $table, $allRows, __METHOD__, [ 'IGNORE' ] );
1936  }
1937  }
1938 
1943  protected function checkDbIsSupported() {
1944  if ( !in_array( $this->db->getType(), $this->supportedDBs ) ) {
1945  throw new MWException( $this->db->getType() . " is not currently supported for unit testing." );
1946  }
1947  }
1948 
1954  public function getCliArg( $offset ) {
1955  return $this->cliArgs[$offset] ?? null;
1956  }
1957 
1963  public function setCliArg( $offset, $value ) {
1964  $this->cliArgs[$offset] = $value;
1965  }
1966 
1974  public function hideDeprecated( $function ) {
1975  Wikimedia\suppressWarnings();
1976  wfDeprecated( $function );
1977  Wikimedia\restoreWarnings();
1978  }
1979 
2000  protected function assertSelect(
2001  $table, $fields, $condition, array $expectedRows, array $options = [], array $join_conds = []
2002  ) {
2003  if ( !$this->needsDB() ) {
2004  throw new MWException( 'When testing database state, the test cases\'s needDB()' .
2005  ' method should return true. Use @group Database or $this->tablesUsed.' );
2006  }
2007 
2008  $db = wfGetDB( DB_REPLICA );
2009 
2010  $res = $db->select(
2011  $table,
2012  $fields,
2013  $condition,
2014  wfGetCaller(),
2015  $options + [ 'ORDER BY' => $fields ],
2016  $join_conds
2017  );
2018  $this->assertNotEmpty( $res, "query failed: " . $db->lastError() );
2019 
2020  $i = 0;
2021 
2022  foreach ( $expectedRows as $expected ) {
2023  $r = $res->fetchRow();
2024  self::stripStringKeys( $r );
2025 
2026  $i += 1;
2027  $this->assertNotEmpty( $r, "row #$i missing" );
2028 
2029  $this->assertEquals( $expected, $r, "row #$i mismatches" );
2030  }
2031 
2032  $r = $res->fetchRow();
2033  self::stripStringKeys( $r );
2034 
2035  $this->assertFalse( $r, "found extra row (after #$i)" );
2036  }
2037 
2049  protected function arrayWrap( array $elements ) {
2050  return array_map(
2051  function ( $element ) {
2052  return [ $element ];
2053  },
2054  $elements
2055  );
2056  }
2057 
2070  protected function assertArrayEquals( array $expected, array $actual,
2071  $ordered = false, $named = false
2072  ) {
2073  if ( !$ordered ) {
2074  $this->objectAssociativeSort( $expected );
2075  $this->objectAssociativeSort( $actual );
2076  }
2077 
2078  if ( !$named ) {
2079  $expected = array_values( $expected );
2080  $actual = array_values( $actual );
2081  }
2082 
2083  call_user_func_array(
2084  [ $this, 'assertEquals' ],
2085  array_merge( [ $expected, $actual ], array_slice( func_get_args(), 4 ) )
2086  );
2087  }
2088 
2101  protected function assertHTMLEquals( $expected, $actual, $msg = '' ) {
2102  $expected = str_replace( '>', ">\n", $expected );
2103  $actual = str_replace( '>', ">\n", $actual );
2104 
2105  $this->assertEquals( $expected, $actual, $msg );
2106  }
2107 
2115  protected function objectAssociativeSort( array &$array ) {
2116  uasort(
2117  $array,
2118  function ( $a, $b ) {
2119  return serialize( $a ) <=> serialize( $b );
2120  }
2121  );
2122  }
2123 
2133  protected static function stripStringKeys( &$r ) {
2134  if ( !is_array( $r ) ) {
2135  return;
2136  }
2137 
2138  foreach ( $r as $k => $v ) {
2139  if ( is_string( $k ) ) {
2140  unset( $r[$k] );
2141  }
2142  }
2143  }
2144 
2158  protected function assertTypeOrValue( $type, $actual, $value = false, $message = '' ) {
2159  if ( $actual === $value ) {
2160  $this->assertTrue( true, $message );
2161  } else {
2162  $this->assertType( $type, $actual, $message );
2163  }
2164  }
2165 
2177  protected function assertType( $type, $actual, $message = '' ) {
2178  if ( class_exists( $type ) || interface_exists( $type ) ) {
2179  $this->assertInstanceOf( $type, $actual, $message );
2180  } else {
2181  $this->assertInternalType( $type, $actual, $message );
2182  }
2183  }
2184 
2194  protected function isWikitextNS( $ns ) {
2196 
2197  if ( isset( $wgNamespaceContentModels[$ns] ) ) {
2199  }
2200 
2201  return true;
2202  }
2203 
2211  protected function getDefaultWikitextNS() {
2213 
2214  static $wikitextNS = null; // this is not going to change
2215  if ( $wikitextNS !== null ) {
2216  return $wikitextNS;
2217  }
2218 
2219  // quickly short out on most common case:
2220  if ( !isset( $wgNamespaceContentModels[NS_MAIN] ) ) {
2221  return NS_MAIN;
2222  }
2223 
2224  // NOTE: prefer content namespaces
2225  $namespaces = array_unique( array_merge(
2227  [ NS_MAIN, NS_HELP, NS_PROJECT ], // prefer these
2229  ) );
2230 
2231  $namespaces = array_diff( $namespaces, [
2232  NS_FILE, NS_CATEGORY, NS_MEDIAWIKI, NS_USER // don't mess with magic namespaces
2233  ] );
2234 
2235  $talk = array_filter( $namespaces, function ( $ns ) {
2236  return MWNamespace::isTalk( $ns );
2237  } );
2238 
2239  // prefer non-talk pages
2240  $namespaces = array_diff( $namespaces, $talk );
2241  $namespaces = array_merge( $namespaces, $talk );
2242 
2243  // check default content model of each namespace
2244  foreach ( $namespaces as $ns ) {
2245  if ( !isset( $wgNamespaceContentModels[$ns] ) ||
2247  ) {
2248  $wikitextNS = $ns;
2249 
2250  return $wikitextNS;
2251  }
2252  }
2253 
2254  // give up
2255  // @todo Inside a test, we could skip the test as incomplete.
2256  // But frequently, this is used in fixture setup.
2257  throw new MWException( "No namespace defaults to wikitext!" );
2258  }
2259 
2266  protected function markTestSkippedIfNoDiff3() {
2267  global $wgDiff3;
2268 
2269  # This check may also protect against code injection in
2270  # case of broken installations.
2271  Wikimedia\suppressWarnings();
2272  $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
2273  Wikimedia\restoreWarnings();
2274 
2275  if ( !$haveDiff3 ) {
2276  $this->markTestSkipped( "Skip test, since diff3 is not configured" );
2277  }
2278  }
2279 
2288  protected function checkPHPExtension( $extName ) {
2289  $loaded = extension_loaded( $extName );
2290  if ( !$loaded ) {
2291  $this->markTestSkipped( "PHP extension '$extName' is not loaded, skipping." );
2292  }
2293 
2294  return $loaded;
2295  }
2296 
2303  protected function markTestSkippedIfDbType( $type ) {
2304  if ( $this->db->getType() === $type ) {
2305  $this->markTestSkipped( "The $type database type isn't supported for this test" );
2306  }
2307  }
2308 
2314  public static function wfResetOutputBuffersBarrier( $buffer ) {
2315  return $buffer;
2316  }
2317 
2325  protected function setTemporaryHook( $hookName, $handler ) {
2326  $this->mergeMwGlobalArrayValue( 'wgHooks', [ $hookName => [ $handler ] ] );
2327  }
2328 
2338  protected function assertFileContains(
2339  $fileName,
2340  $actualData,
2341  $createIfMissing = false,
2342  $msg = ''
2343  ) {
2344  if ( $createIfMissing ) {
2345  if ( !file_exists( $fileName ) ) {
2346  file_put_contents( $fileName, $actualData );
2347  $this->markTestSkipped( 'Data file $fileName does not exist' );
2348  }
2349  } else {
2350  self::assertFileExists( $fileName );
2351  }
2352  self::assertEquals( file_get_contents( $fileName ), $actualData, $msg );
2353  }
2354 
2367  protected function editPage( $pageName, $text, $summary = '', $defaultNs = NS_MAIN ) {
2368  if ( !$this->needsDB() ) {
2369  throw new MWException( 'When testing which pages, the test cases\'s needsDB()' .
2370  ' method should return true. Use @group Database or $this->tablesUsed.' );
2371  }
2372 
2373  $title = Title::newFromText( $pageName, $defaultNs );
2374  $page = WikiPage::factory( $title );
2375 
2376  return $page->doEditContent( ContentHandler::makeContent( $text, $title ), $summary );
2377  }
2378 
2387  protected function revisionDelete(
2388  $rev, array $value = [ Revision::DELETED_TEXT => 1 ], $comment = ''
2389  ) {
2390  if ( is_int( $rev ) ) {
2392  }
2394  'revision', RequestContext::getMain(), $rev->getTitle(), [ $rev->getId() ]
2395  )->setVisibility( [
2396  'value' => $value,
2397  'comment' => $comment,
2398  ] );
2399  }
2400 }
MediaWikiTestCase\$localServices
MediaWikiServices $localServices
The local service locator, created during setUp().
Definition: MediaWikiTestCase.php:33
Wikimedia\Rdbms\Database\tablePrefix
tablePrefix( $prefix=null)
Get/set the table prefix.
Definition: Database.php:604
ObjectCache\clear
static clear()
Clear all the cached instances.
Definition: ObjectCache.php:399
$status
Status::newGood()` to allow deletion, and then `return false` from the hook function. Ensure you consume the 'ChangeTagAfterDelete' hook to carry out custom deletion actions. $tag:name of the tag $user:user initiating the action & $status:Status object. See above. 'ChangeTagsListActive':Allows you to nominate which of the tags your extension uses are in active use. & $tags:list of all active tags. Append to this array. 'ChangeTagsAfterUpdateTags':Called after tags have been updated with the ChangeTags::updateTags function. Params:$addedTags:tags effectively added in the update $removedTags:tags effectively removed in the update $prevTags:tags that were present prior to the update $rc_id:recentchanges table id $rev_id:revision table id $log_id:logging table id $params:tag params $rc:RecentChange being tagged when the tagging accompanies the action, or null $user:User who performed the tagging when the tagging is subsequent to the action, or null 'ChangeTagsAllowedAdd':Called when checking if a user can add tags to a change. & $allowedTags:List of all the tags the user is allowed to add. Any tags the user wants to add( $addTags) that are not in this array will cause it to fail. You may add or remove tags to this array as required. $addTags:List of tags user intends to add. $user:User who is adding the tags. 'ChangeUserGroups':Called before user groups are changed. $performer:The User who will perform the change $user:The User whose groups will be changed & $add:The groups that will be added & $remove:The groups that will be removed 'Collation::factory':Called if $wgCategoryCollation is an unknown collation. $collationName:Name of the collation in question & $collationObject:Null. Replace with a subclass of the Collation class that implements the collation given in $collationName. 'ConfirmEmailComplete':Called after a user 's email has been confirmed successfully. $user:user(object) whose email is being confirmed 'ContentAlterParserOutput':Modify parser output for a given content object. Called by Content::getParserOutput after parsing has finished. Can be used for changes that depend on the result of the parsing but have to be done before LinksUpdate is called(such as adding tracking categories based on the rendered HTML). $content:The Content to render $title:Title of the page, as context $parserOutput:ParserOutput to manipulate 'ContentGetParserOutput':Customize parser output for a given content object, called by AbstractContent::getParserOutput. May be used to override the normal model-specific rendering of page content. $content:The Content to render $title:Title of the page, as context $revId:The revision ID, as context $options:ParserOptions for rendering. To avoid confusing the parser cache, the output can only depend on parameters provided to this hook function, not on global state. $generateHtml:boolean, indicating whether full HTML should be generated. If false, generation of HTML may be skipped, but other information should still be present in the ParserOutput object. & $output:ParserOutput, to manipulate or replace 'ContentHandlerDefaultModelFor':Called when the default content model is determined for a given title. May be used to assign a different model for that title. $title:the Title in question & $model:the model name. Use with CONTENT_MODEL_XXX constants. 'ContentHandlerForModelID':Called when a ContentHandler is requested for a given content model name, but no entry for that model exists in $wgContentHandlers. Note:if your extension implements additional models via this hook, please use GetContentModels hook to make them known to core. $modeName:the requested content model name & $handler:set this to a ContentHandler object, if desired. 'ContentModelCanBeUsedOn':Called to determine whether that content model can be used on a given page. This is especially useful to prevent some content models to be used in some special location. $contentModel:ID of the content model in question $title:the Title in question. & $ok:Output parameter, whether it is OK to use $contentModel on $title. Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok. 'ContribsPager::getQueryInfo':Before the contributions query is about to run & $pager:Pager object for contributions & $queryInfo:The query for the contribs Pager 'ContribsPager::reallyDoQuery':Called before really executing the query for My Contributions & $data:an array of results of all contribs queries $pager:The ContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'ContributionsLineEnding':Called before a contributions HTML line is finished $page:SpecialPage object for contributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'ContributionsToolLinks':Change tool links above Special:Contributions $id:User identifier $title:User page title & $tools:Array of tool links $specialPage:SpecialPage instance for context and services. Can be either SpecialContributions or DeletedContributionsPage. Extensions should type hint against a generic SpecialPage though. 'ConvertContent':Called by AbstractContent::convert when a conversion to another content model is requested. Handler functions that modify $result should generally return false to disable further attempts at conversion. $content:The Content object to be converted. $toModel:The ID of the content model to convert to. $lossy:boolean indicating whether lossy conversion is allowed. & $result:Output parameter, in case the handler function wants to provide a converted Content object. Note that $result->getContentModel() must return $toModel. 'ContentSecurityPolicyDefaultSource':Modify the allowed CSP load sources. This affects all directives except for the script directive. If you want to add a script source, see ContentSecurityPolicyScriptSource hook. & $defaultSrc:Array of Content-Security-Policy allowed sources $policyConfig:Current configuration for the Content-Security-Policy header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'ContentSecurityPolicyDirectives':Modify the content security policy directives. Use this only if ContentSecurityPolicyDefaultSource and ContentSecurityPolicyScriptSource do not meet your needs. & $directives:Array of CSP directives $policyConfig:Current configuration for the CSP header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'ContentSecurityPolicyScriptSource':Modify the allowed CSP script sources. Note that you also have to use ContentSecurityPolicyDefaultSource if you want non-script sources to be loaded from whatever you add. & $scriptSrc:Array of CSP directives $policyConfig:Current configuration for the CSP header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'CustomEditor':When invoking the page editor Return true to allow the normal editor to be used, or false if implementing a custom editor, e.g. for a special namespace, etc. $article:Article being edited $user:User performing the edit 'DatabaseOraclePostInit':Called after initialising an Oracle database $db:the DatabaseOracle object 'DeletedContribsPager::reallyDoQuery':Called before really executing the query for Special:DeletedContributions Similar to ContribsPager::reallyDoQuery & $data:an array of results of all contribs queries $pager:The DeletedContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'DeletedContributionsLineEnding':Called before a DeletedContributions HTML line is finished. Similar to ContributionsLineEnding $page:SpecialPage object for DeletedContributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'DeleteUnknownPreferences':Called by the cleanupPreferences.php maintenance script to build a WHERE clause with which to delete preferences that are not known about. This hook is used by extensions that have dynamically-named preferences that should not be deleted in the usual cleanup process. For example, the Gadgets extension creates preferences prefixed with 'gadget-', and so anything with that prefix is excluded from the deletion. &where:An array that will be passed as the $cond parameter to IDatabase::select() to determine what will be deleted from the user_properties table. $db:The IDatabase object, useful for accessing $db->buildLike() etc. 'DifferenceEngineAfterLoadNewText':called in DifferenceEngine::loadNewText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before returning true from this function. $differenceEngine:DifferenceEngine object 'DifferenceEngineLoadTextAfterNewContentIsLoaded':called in DifferenceEngine::loadText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before checking if the variable 's value is null. This hook can be used to inject content into said class member variable. $differenceEngine:DifferenceEngine object 'DifferenceEngineMarkPatrolledLink':Allows extensions to change the "mark as patrolled" link which is shown both on the diff header as well as on the bottom of a page, usually wrapped in a span element which has class="patrollink". $differenceEngine:DifferenceEngine object & $markAsPatrolledLink:The "mark as patrolled" link HTML(string) $rcid:Recent change ID(rc_id) for this change(int) 'DifferenceEngineMarkPatrolledRCID':Allows extensions to possibly change the rcid parameter. For example the rcid might be set to zero due to the user being the same as the performer of the change but an extension might still want to show it under certain conditions. & $rcid:rc_id(int) of the change or 0 $differenceEngine:DifferenceEngine object $change:RecentChange object $user:User object representing the current user 'DifferenceEngineNewHeader':Allows extensions to change the $newHeader variable, which contains information about the new revision, such as the revision 's author, whether the revision was marked as a minor edit or not, etc. $differenceEngine:DifferenceEngine object & $newHeader:The string containing the various #mw-diff-otitle[1-5] divs, which include things like revision author info, revision comment, RevisionDelete link and more $formattedRevisionTools:Array containing revision tools, some of which may have been injected with the DiffRevisionTools hook $nextlink:String containing the link to the next revision(if any) $status
Definition: hooks.txt:1266
MediaWikiTestCase\getTestPrefixFor
static getTestPrefixFor(IDatabase $db)
Definition: MediaWikiTestCase.php:1182
MediaWiki\Logger\LegacySpi
LoggerFactory service provider that creates LegacyLogger instances.
Definition: LegacySpi.php:37
MediaWikiTestCase\$reuseDB
static $reuseDB
Definition: MediaWikiTestCase.php:70
MediaWiki\Logger\MonologSpi
LoggerFactory service provider that creates loggers implemented by Monolog.
Definition: MonologSpi.php:116
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:48
$user
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1476
FauxRequest
WebRequest clone which takes values from a provided array.
Definition: FauxRequest.php:33
$wgNamespaceContentModels
$wgNamespaceContentModels
Associative array mapping namespace IDs to the name of the content model pages in that namespace shou...
Definition: DefaultSettings.php:8567
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:306
MediaWikiTestCase\assertArrayEquals
assertArrayEquals(array $expected, array $actual, $ordered=false, $named=false)
Assert that two arrays are equal.
Definition: MediaWikiTestCase.php:2070
MediaWikiTestResult
Definition: MediaWikiTestResult.php:3
MediaWikiTestCase\copyTestData
copyTestData(IDatabase $source, IDatabase $target)
Copy test data from one database connection to another.
Definition: MediaWikiTestCase.php:1914
Language\clearCaches
static clearCaches()
Intended for tests that may change configuration in a way that invalidates caches.
Definition: Language.php:284
MediaWikiTestCase\stashMwGlobals
stashMwGlobals( $globalKeys)
Stashes the global, will be restored in tearDown()
Definition: MediaWikiTestCase.php:816
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
NS_HELP
const NS_HELP
Definition: Defines.php:76
MWNamespace\getValidNamespaces
static getValidNamespaces()
Returns an array of the namespaces (by integer id) that exist on the wiki.
Definition: MWNamespace.php:287
User\resetGetDefaultOptionsForTestsOnly
static resetGetDefaultOptionsForTestsOnly()
Reset the process cache of default user options.
Definition: User.php:1764
Revision\newFromId
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:118
Wikimedia\Rdbms\Database\listTables
listTables( $prefix=null, $fname=__METHOD__)
List all tables on the database.
Definition: Database.php:4124
MWNamespace\isTalk
static isTalk( $index)
Is the given namespace a talk namespace?
Definition: MWNamespace.php:119
MediaWikiTestCase\mergeMwGlobalArrayValue
mergeMwGlobalArrayValue( $name, $values)
Merges the given values into a MW global array variable.
Definition: MediaWikiTestCase.php:904
MediaWikiTestCase\getSchemaOverrides
getSchemaOverrides(IMaintainableDatabase $db)
Stub.
Definition: MediaWikiTestCase.php:1588
MediaWikiTestCase\restoreLoggers
restoreLoggers()
Restores loggers replaced by setLogger().
Definition: MediaWikiTestCase.php:1146
MediaWikiTestCase\getTestUser
static getTestUser( $groups=[])
Convenience method for getting an immutable test user.
Definition: MediaWikiTestCase.php:180
MediaWikiTestCase\wfResetOutputBuffersBarrier
static wfResetOutputBuffersBarrier( $buffer)
Used as a marker to prevent wfResetOutputBuffers from breaking PHPUnit.
Definition: MediaWikiTestCase.php:2314
MediaWikiTestCase\stripStringKeys
static stripStringKeys(&$r)
Utility function for eliminating all string keys from an array.
Definition: MediaWikiTestCase.php:2133
MultiConfig
Provides a fallback sequence for Config objects.
Definition: MultiConfig.php:28
wfMkdirParents
wfMkdirParents( $dir, $mode=null, $caller=null)
Make directory, and make all parent directories if they don't exist.
Definition: GlobalFunctions.php:2008
MediaWikiTestCase\makeTestConfigFactoryInstantiator
static makeTestConfigFactoryInstantiator(ConfigFactory $oldFactory, array $configurations)
Definition: MediaWikiTestCase.php:332
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:103
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
HashConfig
A Config instance which stores all settings as a member variable.
Definition: HashConfig.php:28
MediaWikiTestCase\$tmpFiles
array $tmpFiles
Holds the paths of temporary files/directories created through getNewTempFile, and getNewTempDirector...
Definition: MediaWikiTestCase.php:87
MediaWikiTestCase\setUpBeforeClass
static setUpBeforeClass()
Definition: MediaWikiTestCase.php:163
$wgDiff3
$wgDiff3
Path to the GNU diff3 utility.
Definition: DefaultSettings.php:6661
MediaWikiTestCase\dropMockTables
dropMockTables(IMaintainableDatabase $db, array $tables)
Drops the given mock tables.
Definition: MediaWikiTestCase.php:1704
MediaWiki\Logger\LogCapturingSpi
Wraps another spi to capture all logs generated.
Definition: LogCapturingSpi.php:13
CACHE_NONE
const CACHE_NONE
Definition: Defines.php:102
MediaWikiTestCase\setupAllTestDBs
setupAllTestDBs()
Set up all test DBs.
Definition: MediaWikiTestCase.php:1426
MediaWikiTestCase\isWikitextNS
isWikitextNS( $ns)
Returns true if the given namespace defaults to Wikitext according to $wgNamespaceContentModels.
Definition: MediaWikiTestCase.php:2194
Maintenance\setLBFactoryTriggers
static setLBFactoryTriggers(LBFactory $LBFactory, Config $config)
Definition: Maintenance.php:670
MediaWikiTestCase\needsDB
needsDB()
Definition: MediaWikiTestCase.php:1190
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUnknownUser':When a user doesn 't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name:User name 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED since 1.28! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1983
CACHE_MEMCACHED
const CACHE_MEMCACHED
Definition: Defines.php:104
$namespaces
namespace and then decline to actually register it & $namespaces
Definition: hooks.txt:925
Wikimedia\Rdbms\IDatabase\lastError
lastError()
Get a description of the last error.
MediaWikiTestCase\$useTemporaryTables
static $useTemporaryTables
Definition: MediaWikiTestCase.php:69
$tables
this hook is for auditing only RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist & $tables
Definition: hooks.txt:979
CACHE_ACCEL
const CACHE_ACCEL
Definition: Defines.php:105
NS_FILE
const NS_FILE
Definition: Defines.php:70
MediaWikiTestCase\checkDbIsSupported
checkDbIsSupported()
Definition: MediaWikiTestCase.php:1943
MediaWikiTestCase\setupDatabaseWithTestPrefix
static setupDatabaseWithTestPrefix(IMaintainableDatabase $db, $prefix=null)
Setups a database with cloned tables using the given prefix.
Definition: MediaWikiTestCase.php:1388
MediaWikiTestCase\getCliArg
getCliArg( $offset)
Definition: MediaWikiTestCase.php:1954
MediaWikiTestCase\ensureMockDatabaseConnection
ensureMockDatabaseConnection(IDatabase $db)
Definition: MediaWikiTestCase.php:1559
MediaWikiTestCase\testMediaWikiTestCaseParentSetupCalled
testMediaWikiTestCaseParentSetupCalled()
Make sure MediaWikiTestCase extending classes have called their parent setUp method.
Definition: MediaWikiTestCase.php:634
MediaWikiTestCase\isNotUnittest
static isNotUnittest( $table)
Definition: MediaWikiTestCase.php:1866
$res
$res
Definition: database.txt:21
JobQueueGroup\destroySingletons
static destroySingletons()
Destroy the singleton instances.
Definition: JobQueueGroup.php:98
DeferredUpdates\clearPendingUpdates
static clearPendingUpdates()
Clear all pending updates without performing them.
Definition: DeferredUpdates.php:377
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:235
MediaWikiTestCase\$schemaOverrideDefaults
static $schemaOverrideDefaults
Definition: MediaWikiTestCase.php:1567
MediaWikiTestCase\insertPage
insertPage( $pageName, $text='Sample page for unit test.', $namespace=null, User $user=null)
Insert a new page.
Definition: MediaWikiTestCase.php:1222
MediaWikiTestCase\prepareServices
static prepareServices(Config $bootstrapConfig)
Definition: MediaWikiTestCase.php:276
$wgDefaultExternalStore
array $wgDefaultExternalStore
The place to put new revisions, false to put them in the local text table.
Definition: DefaultSettings.php:2236
serialize
serialize()
Definition: ApiMessageTrait.php:134
MWNamespace\getContentNamespaces
static getContentNamespaces()
Get a list of all namespace indices which are considered to contain content.
Definition: MWNamespace.php:375
Wikimedia\Rdbms\IDatabase\insert
insert( $table, $a, $fname=__METHOD__, $options=[])
INSERT wrapper, inserts an array into a table.
$wgDBprefix
$wgDBprefix
Table name prefix.
Definition: DefaultSettings.php:1941
$wgJobClasses
$wgJobClasses['replaceText']
Definition: ReplaceText.php:52
MediaWikiTestCase\addDBDataOnce
addDBDataOnce()
Stub.
Definition: MediaWikiTestCase.php:1268
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
MediaWikiTestCase\teardownTestDB
static teardownTestDB()
Restores MediaWiki to using the table set (table prefix) it was using before setupTestDB() was called...
Definition: MediaWikiTestCase.php:1354
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
TestUser
Wraps the user object, so we can also retain full access to properties like password if we log in via...
Definition: TestUser.php:9
MediaWikiTestCase\assertFileContains
assertFileContains( $fileName, $actualData, $createIfMissing=false, $msg='')
Check whether file contains given data.
Definition: MediaWikiTestCase.php:2338
MediaWikiTestCase\overrideMwServices
overrideMwServices(Config $configOverrides=null, array $services=[])
Stashes the global instance of MediaWikiServices, and installs a new one, allowing test cases to over...
Definition: MediaWikiTestCase.php:937
FileBackendGroup\destroySingleton
static destroySingleton()
Destroy the singleton instance.
Definition: FileBackendGroup.php:58
MediaWikiTestCase\$called
$called
$called tracks whether the setUp and tearDown method has been called.
Definition: MediaWikiTestCase.php:47
NS_MAIN
const NS_MAIN
Definition: Defines.php:64
MWNamespace\clearCaches
static clearCaches()
Clear internal caches.
Definition: MWNamespace.php:77
MediaWikiTestCase\getExternalStoreDatabaseConnections
static getExternalStoreDatabaseConnections()
Gets master database connections for all of the ExternalStoreDB stores configured in $wgDefaultExtern...
Definition: MediaWikiTestCase.php:1513
MediaWikiTestCase\getNonexistingTestPage
getNonexistingTestPage( $title=null)
Returns a WikiPage representing a non-existing page.
Definition: MediaWikiTestCase.php:256
Config
Interface for configuration instances.
Definition: Config.php:28
$data
$data
Utility to generate mapping file used in mw.Title (phpCharToUpper.json)
Definition: generatePhpCharToUpperMappings.php:13
MediaWikiTestCase\undoSchemaOverrides
undoSchemaOverrides(IMaintainableDatabase $db, $oldOverrides)
Undoes the specified schema overrides.
Definition: MediaWikiTestCase.php:1599
MediaWikiTestCase\truncateTable
truncateTable( $tableName, IDatabase $db=null)
Empties the given table and resets any auto-increment counters.
Definition: MediaWikiTestCase.php:1834
Wikimedia\Rdbms\Database\delete
delete( $table, $conds, $fname=__METHOD__)
DELETE query wrapper.
Definition: Database.php:3015
MWException
MediaWiki exception.
Definition: MWException.php:26
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:925
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:138
NS_PROJECT
const NS_PROJECT
Definition: Defines.php:68
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1078
MediaWikiTestCase\$phpErrorLevel
int $phpErrorLevel
Original value of PHP's error_reporting setting.
Definition: MediaWikiTestCase.php:79
MediaWikiTestCase\recloneMockTables
recloneMockTables(IMaintainableDatabase $db, array $tables)
Re-clones the given mock tables to restore them based on the live database schema.
Definition: MediaWikiTestCase.php:1754
MediaWikiTestCase\installMockMwServices
static installMockMwServices(Config $configOverrides=null)
Creates a new "mock" MediaWikiServices instance, and installs it.
Definition: MediaWikiTestCase.php:978
$handler
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not it can be in the form of< username >< more info > e g for bot passwords intended to be added to log contexts Fields it might only if the login was with a bot password it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check set to true or false to override the $wgMaxImageArea check result gives extension the possibility to transform it themselves $handler
Definition: hooks.txt:780
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2636
MediaWikiTestCase\setMwGlobals
setMwGlobals( $pairs, $value=null)
Sets a global, maintaining a stashed version of the previous global to be restored in tearDown.
Definition: MediaWikiTestCase.php:709
MediaWikiTestCase\getNewTempFile
getNewTempFile()
Obtains a new temporary file name.
Definition: MediaWikiTestCase.php:474
MediaWikiTestCase\addCoreDBData
addCoreDBData()
Definition: MediaWikiTestCase.php:1286
MediaWikiTestCase\run
run(PHPUnit_Framework_TestResult $result=null)
Definition: MediaWikiTestCase.php:385
MediaWikiTestCase
Definition: MediaWikiTestCase.php:17
MediaWikiTestCase\$oldTablePrefix
static $oldTablePrefix
Definition: MediaWikiTestCase.php:72
MediaWikiTestCase\hideDeprecated
hideDeprecated( $function)
Don't throw a warning if $function is deprecated and called later.
Definition: MediaWikiTestCase.php:1974
MediaWikiTestCase\isUsingExternalStoreDB
static isUsingExternalStoreDB()
Check whether ExternalStoreDB is being used.
Definition: MediaWikiTestCase.php:1537
MediaWikiTestCase\assertSelect
assertSelect( $table, $fields, $condition, array $expectedRows, array $options=[], array $join_conds=[])
Asserts that the given database query yields the rows given by $expectedRows.
Definition: MediaWikiTestCase.php:2000
MediaWikiTestCase\listTables
static listTables(IMaintainableDatabase $db)
Definition: MediaWikiTestCase.php:1877
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
MediaWikiTestCase\$users
static TestUser[] $users
Definition: MediaWikiTestCase.php:53
MediaWikiTestCase\$iniSettings
array $iniSettings
Holds original values of ini settings to be restored in tearDown().
Definition: MediaWikiTestCase.php:110
MWDebug\clearLog
static clearLog()
Clears internal log array and deprecation tracking.
Definition: MWDebug.php:135
MediaWikiTestCase\resetNamespaces
resetNamespaces()
Must be called whenever namespaces are changed, e.g., $wgExtraNamespaces is altered.
Definition: MediaWikiTestCase.php:755
MediaWikiTestCase\getDefaultWikitextNS
getDefaultWikitextNS()
Returns the ID of a namespace that defaults to Wikitext.
Definition: MediaWikiTestCase.php:2211
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:78
MediaWikiTestCase\setUserLang
setUserLang( $lang)
Definition: MediaWikiTestCase.php:1057
DB_MASTER
const DB_MASTER
Definition: defines.php:26
WikitextContent
Content object for wiki text pages.
Definition: WikitextContent.php:36
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
MediaWikiTestCase\__construct
__construct( $name=null, array $data=[], $dataName='')
Definition: MediaWikiTestCase.php:148
MediaWiki\Session\SessionManager\resetCache
static resetCache()
Reset the internal caching for unit testing.
Definition: SessionManager.php:934
RevisionDeleter\createList
static createList( $typeName, IContextSource $context, Title $title, array $ids)
Instantiate the appropriate list class for a given list of IDs.
Definition: RevisionDeleter.php:83
MediaWiki\Auth\AuthManager\resetCache
static resetCache()
Reset the internal caching for unit testing.
Definition: AuthManager.php:2452
MediaWikiTestCase\arrayWrap
arrayWrap(array $elements)
Utility method taking an array of elements and wrapping each element in its own array.
Definition: MediaWikiTestCase.php:2049
run
and give any other recipients of the Program a copy of this License along with the Program You may charge a fee for the physical act of transferring a and you may at your option offer warranty protection in exchange for a fee You may modify your copy or copies of the Program or any portion of thus forming a work based on the and copy and distribute such modifications or work under the terms of Section provided that you also meet all of these that in whole or in part contains or is derived from the Program or any part to be licensed as a whole at no charge to all third parties under the terms of this License c If the modified program normally reads commands interactively when run
Definition: COPYING.txt:87
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
MediaWikiTestCase\mungeSchemaUpdateQuery
mungeSchemaUpdateQuery( $cmd)
Definition: MediaWikiTestCase.php:1692
ContentHandler\makeContent
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
Definition: ContentHandler.php:133
null
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:780
MediaWikiTestCase\setContentLang
setContentLang( $lang)
Definition: MediaWikiTestCase.php:1066
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
MediaWikiTestCase\markTestSkippedIfNoDiff3
markTestSkippedIfNoDiff3()
Check, if $wgDiff3 is set and ready to merge Will mark the calling test as skipped,...
Definition: MediaWikiTestCase.php:2266
MediaWikiTestCase\editPage
editPage( $pageName, $text, $summary='', $defaultNs=NS_MAIN)
Edits or creates a page/revision.
Definition: MediaWikiTestCase.php:2367
RequestContext\resetMain
static resetMain()
Resets singleton returned by getMain().
Definition: RequestContext.php:456
Wikimedia\Rdbms\Database\tableExists
tableExists( $table, $fname=__METHOD__)
Query whether a given table exists.
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2162
MediaWikiTestCase\assertHTMLEquals
assertHTMLEquals( $expected, $actual, $msg='')
Put each HTML element on its own line and then equals() the results.
Definition: MediaWikiTestCase.php:2101
MediaWikiTestCase\addTmpFiles
addTmpFiles( $files)
Definition: MediaWikiTestCase.php:543
$value
$value
Definition: styleTest.css.php:49
MediaWikiTestCase\assertTypeOrValue
assertTypeOrValue( $type, $actual, $value=false, $message='')
Asserts that the provided variable is of the specified internal type or equals the $value argument.
Definition: MediaWikiTestCase.php:2158
MediaWikiTestCase\revisionDelete
revisionDelete( $rev, array $value=[Revision::DELETED_TEXT=> 1], $comment='')
Revision-deletes a revision.
Definition: MediaWikiTestCase.php:2387
LegacySpi
MediaWiki Logger LegacySpi
Definition: logger.txt:53
ConfigFactory\makeConfig
makeConfig( $name)
Create a given Config using the registered callback for $name.
Definition: ConfigFactory.php:129
MediaWikiTestCase\setupExternalStoreTestDBs
static setupExternalStoreTestDBs( $testPrefix=null)
Clones the External Store database(s) for testing.
Definition: MediaWikiTestCase.php:1500
MediaWikiTestCase\getMutableTestUser
static getMutableTestUser( $groups=[])
Convenience method for getting a mutable test user.
Definition: MediaWikiTestCase.php:192
MediaWikiTestCase\dbPrefix
dbPrefix()
Definition: MediaWikiTestCase.php:1173
MediaWikiTestCase\$mwGlobalsToUnset
array $mwGlobalsToUnset
Holds list of MediaWiki configuration settings to be unset in tearDown().
Definition: MediaWikiTestCase.php:102
MediaWikiTestCase\resetDB
resetDB( $db, $tablesUsed)
Empty all tables so they can be repopulated for tests.
Definition: MediaWikiTestCase.php:1776
MediaWikiTestCase\getTestSysop
static getTestSysop()
Convenience method for getting an immutable admin test user.
Definition: MediaWikiTestCase.php:204
Wikimedia\Rdbms\Database\tableName
tableName( $name, $format='quoted')
Format a table name ready for use in constructing an SQL query.
Definition: Database.php:2410
MediaWikiTestCase\isTestInDatabaseGroup
isTestInDatabaseGroup()
Definition: MediaWikiTestCase.php:1199
MediaWikiTestCase\setGroupPermissions
setGroupPermissions( $newPerms, $newKey=null, $newValue=null)
Alters $wgGroupPermissions for the duration of the test.
Definition: MediaWikiTestCase.php:1095
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:430
MediaWikiTestCase\addDBData
addDBData()
Stub.
Definition: MediaWikiTestCase.php:1280
CloneDatabase\useTemporaryTables
useTemporaryTables( $u=true)
Set whether to use temporary tables or not.
Definition: CloneDatabase.php:69
MediaWikiTestCase\setUp
setUp()
Definition: MediaWikiTestCase.php:504
MediaWikiTestCase\restoreMwServices
static restoreMwServices()
Restores the original, non-mock MediaWikiServices instance.
Definition: MediaWikiTestCase.php:1036
MediaWikiTestCase\getExistingTestPage
getExistingTestPage( $title=null)
Returns a WikiPage representing an existing page.
Definition: MediaWikiTestCase.php:220
MediaWikiTestCase\objectAssociativeSort
objectAssociativeSort(array &$array)
Does an associative sort that works for objects.
Definition: MediaWikiTestCase.php:2115
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:152
MediaWikiTestCase\DB_PREFIX
const DB_PREFIX
Table name prefixes.
Definition: MediaWikiTestCase.php:134
unserialize
unserialize( $serialized)
Definition: ApiMessageTrait.php:142
MediaWikiTestCase\$overriddenServices
string[] $overriddenServices
Holds a list of services that were overridden with setService().
Definition: MediaWikiTestCase.php:129
MonologSpi
MediaWiki Logger MonologSpi
Definition: logger.txt:58
wfTempDir
wfTempDir()
Tries to get the system directory for temporary files.
Definition: GlobalFunctions.php:1989
ConfigFactory\getConfigNames
getConfigNames()
Definition: ConfigFactory.php:93
ExternalStore\getStoreObject
static getStoreObject( $proto, array $params=[])
Get an external store object of the given type, with the given parameters.
Definition: ExternalStore.php:56
MediaWikiTestCase\$supportedDBs
array $supportedDBs
Definition: MediaWikiTestCase.php:141
CloneDatabase
Definition: CloneDatabase.php:26
JobQueueGroup\singleton
static singleton( $domain=false)
Definition: JobQueueGroup.php:70
MediaWikiTestCase\canShallowCopy
static canShallowCopy( $value)
Check if we can back up a value by performing a shallow copy.
Definition: MediaWikiTestCase.php:783
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1985
MediaWikiTestCase\ORA_DB_PREFIX
const ORA_DB_PREFIX
Definition: MediaWikiTestCase.php:135
MediaWikiTestCase\setLogger
setLogger( $channel, LoggerInterface $logger)
Sets the logger for a specified channel, for the duration of the test.
Definition: MediaWikiTestCase.php:1118
CloneDatabase\changePrefix
static changePrefix( $prefix)
Change the table prefix on all open DB connections.
Definition: CloneDatabase.php:136
User\resetIdByNameCache
static resetIdByNameCache()
Reset the cache used in idFromName().
Definition: User.php:947
wfRecursiveRemoveDir
wfRecursiveRemoveDir( $dir)
Remove a directory and all its content.
Definition: GlobalFunctions.php:2051
MediaWikiTestCase\makeTestConfig
static makeTestConfig(Config $baseConfig=null, Config $customOverrides=null)
Create a config suitable for testing, based on a base config, default overrides, and custom overrides...
Definition: MediaWikiTestCase.php:288
MediaWikiTestCase\$originalServices
static MediaWikiServices null $originalServices
The original service locator.
Definition: MediaWikiTestCase.php:27
$rev
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
Definition: hooks.txt:1769
MediaWikiTestCase\$tablesUsed
array $tablesUsed
Definition: MediaWikiTestCase.php:67
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
MediaWikiTestCase\resetNonServiceCaches
static resetNonServiceCaches()
Resets some non-service singleton instances and other static caches.
Definition: MediaWikiTestCase.php:361
MediaWikiTestCase\listOriginalTables
listOriginalTables(IMaintainableDatabase $db)
Lists all tables in the live database schema, without a prefix.
Definition: MediaWikiTestCase.php:1719
Wikimedia\Rdbms\IDatabase\getType
getType()
Get the type of the DBMS, as it appears in $wgDBtype.
MediaWikiTestCase\oncePerClass
oncePerClass()
Definition: MediaWikiTestCase.php:443
ConfigFactory
Factory class to create Config objects.
Definition: ConfigFactory.php:31
MediaWikiTestCase\containsClosure
containsClosure( $var, $maxDepth=15)
Definition: MediaWikiTestCase.php:873
NS_USER
const NS_USER
Definition: Defines.php:66
MediaWikiTestCase\checkPHPExtension
checkPHPExtension( $extName)
Check if $extName is a loaded PHP extension, will skip the test whenever it is not loaded.
Definition: MediaWikiTestCase.php:2288
LoggerFactory
MediaWiki Logger LoggerFactory implements a PSR[0] compatible message logging system Named Psr Log LoggerInterface instances can be obtained from the MediaWiki Logger LoggerFactory::getInstance() static method. MediaWiki\Logger\LoggerFactory expects a class implementing the MediaWiki\Logger\Spi interface to act as a factory for new Psr\Log\LoggerInterface instances. The "Spi" in MediaWiki\Logger\Spi stands for "service provider interface". An SPI is an API intended to be implemented or extended by a third party. This software design pattern is intended to enable framework extension and replaceable components. It is specifically used in the MediaWiki\Logger\LoggerFactory service to allow alternate PSR-3 logging implementations to be easily integrated with MediaWiki. The service provider interface allows the backend logging library to be implemented in multiple ways. The $wgMWLoggerDefaultSpi global provides the classname of the default MediaWiki\Logger\Spi implementation to be loaded at runtime. This can either be the name of a class implementing the MediaWiki\Logger\Spi with a zero argument const ructor or a callable that will return an MediaWiki\Logger\Spi instance. Alternately the MediaWiki\Logger\LoggerFactory MediaWiki Logger LoggerFactory
Definition: logger.txt:5
$source
$source
Definition: mwdoc-filter.php:46
MediaWikiTestCase\setupTestDB
static setupTestDB(IMaintainableDatabase $db, $prefix)
Creates an empty skeleton of the wiki database by cloning its structure to equivalent tables using th...
Definition: MediaWikiTestCase.php:1466
Wikimedia\Rdbms\Database\select
select( $table, $vars, $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
Execute a SELECT query constructed using the various parameters provided.
Definition: Database.php:1779
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:72
$wgGroupPermissions
$wgGroupPermissions['sysop']['replacetext']
Definition: ReplaceText.php:56
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
TestUserRegistry\getMutableTestUser
static getMutableTestUser( $testName, $groups=[])
Get a TestUser object that the caller may modify.
Definition: TestUserRegistry.php:34
EDIT_SUPPRESS_RC
const EDIT_SUPPRESS_RC
Definition: Defines.php:155
MediaWikiTestCase\$mwGlobals
array $mwGlobals
Holds original values of MediaWiki configuration settings to be restored in tearDown().
Definition: MediaWikiTestCase.php:95
$services
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title e g db for database replication lag or jobqueue for job queue size converted to pseudo seconds It is possible to add more fields and they will be returned to the user in the API response after the basic globals have been set but before ordinary actions take place or wrap services the preferred way to define a new service is the $wgServiceWiringFiles array $services
Definition: hooks.txt:2220
MediaWikiTestCase\$loggers
LoggerInterface[] $loggers
Holds original loggers which have been replaced by setLogger()
Definition: MediaWikiTestCase.php:116
MediaWikiTestCase\setTemporaryHook
setTemporaryHook( $hookName, $handler)
Create a temporary hook handler which will be reset by tearDown.
Definition: MediaWikiTestCase.php:2325
$wgRequest
if(! $wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:728
MediaWikiServices
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
MediaWikiTestCase\usesTemporaryTables
usesTemporaryTables()
Definition: MediaWikiTestCase.php:461
MediaWikiTestCase\doStashMwGlobals
doStashMwGlobals( $globalKeys)
Definition: MediaWikiTestCase.php:821
MediaWikiTestCase\doSetMwGlobals
doSetMwGlobals( $pairs, $value=null)
An internal method that allows setService() to set globals that tests are not supposed to touch.
Definition: MediaWikiTestCase.php:727
MediaWikiTestCase\assertType
assertType( $type, $actual, $message='')
Asserts the type of the provided value.
Definition: MediaWikiTestCase.php:2177
SiteStatsInit\doPlaceholderInit
static doPlaceholderInit()
Insert a dummy row with all zeroes if no row is present.
Definition: SiteStatsInit.php:157
Wikimedia\Rdbms\Database\sourceFile
sourceFile( $filename, callable $lineCallback=null, callable $resultCallback=null, $fname=false, callable $inputCallback=null)
Read and execute SQL commands from a file.
Definition: Database.php:4344
TestUserRegistry\getImmutableTestUser
static getImmutableTestUser( $groups=[])
Get a TestUser object that the caller may not modify.
Definition: TestUserRegistry.php:60
MediaWikiTestCase\setCliArg
setCliArg( $offset, $value)
Definition: MediaWikiTestCase.php:1963
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:48
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
MediaWikiTestCase\setService
setService( $name, $object)
Sets a service, maintaining a stashed version of the previous service to be restored in tearDown.
Definition: MediaWikiTestCase.php:649
Wikimedia\Rdbms\Database\listViews
listViews( $prefix=null, $fname=__METHOD__)
Lists all the VIEWs in the database.
Definition: Database.php:4128
MediaWikiTestCase\markTestSkippedIfDbType
markTestSkippedIfDbType( $type)
Skip the test if using the specified database type.
Definition: MediaWikiTestCase.php:2303
wfGetCaller
wfGetCaller( $level=2)
Get the name of the function which called this function wfGetCaller( 1 ) is the function with the wfG...
Definition: GlobalFunctions.php:1480
Wikimedia\Rdbms\IMaintainableDatabase
Advanced database interface for IDatabase handles that include maintenance methods.
Definition: IMaintainableDatabase.php:38
Wikimedia\Rdbms\Database\query
query( $sql, $fname=__METHOD__, $flags=0)
Run an SQL query and return the result.
Definition: Database.php:1191
MediaWikiTestCase\__destruct
__destruct()
Definition: MediaWikiTestCase.php:155
MediaWikiTestCase\setIniSetting
setIniSetting( $name, $value)
Set an ini setting for the duration of the test.
Definition: MediaWikiTestCase.php:745
Revision\DELETED_TEXT
const DELETED_TEXT
Definition: Revision.php:46
Language
Internationalisation code.
Definition: Language.php:36
MediaWikiTestCase\unprefixTable
static unprefixTable(&$tableName, $ind, $prefix)
Definition: MediaWikiTestCase.php:1862
$GLOBALS
$GLOBALS['IP']
Definition: ComposerHookHandler.php:6
MediaWikiTestCase\$db
Database $db
Primary database.
Definition: MediaWikiTestCase.php:61
MediaWikiTestCase\$cliArgs
array $cliArgs
The CLI arguments passed through from phpunit.php.
Definition: MediaWikiTestCase.php:122
$buffer
$buffer
Definition: mwdoc-filter.php:49
CACHE_DB
const CACHE_DB
Definition: Defines.php:103
MediaWikiTestCase\tearDown
tearDown()
Definition: MediaWikiTestCase.php:547
MediaWikiTestCase\setUpSchema
setUpSchema(IMaintainableDatabase $db)
Applies the schema overrides returned by getSchemaOverrides(), after undoing any previously applied s...
Definition: MediaWikiTestCase.php:1631
MediaWikiTestCase\$dbSetup
static $dbSetup
Definition: MediaWikiTestCase.php:71
$wgSQLMode
$wgSQLMode
SQL Mode - default is turning off all modes, including strict, if set.
Definition: DefaultSettings.php:1954
TestUserRegistry\clear
static clear()
Clear the registry.
Definition: TestUserRegistry.php:105
$type
$type
Definition: testCompression.php:48
MediaWikiTestCase\getNewTempDirectory
getNewTempDirectory()
obtains a new temporary directory
Definition: MediaWikiTestCase.php:491