MediaWiki  REL1_31
MediaWikiTestCase.php
Go to the documentation of this file.
1 <?php
2 
7 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 
29  private static $serviceLocator = null;
30 
43  private $called = [];
44 
49  public static $users;
50 
57  protected $db;
58 
63  protected $tablesUsed = []; // tables with data
64 
65  private static $useTemporaryTables = true;
66  private static $reuseDB = false;
67  private static $dbSetup = false;
68  private static $oldTablePrefix = '';
69 
75  private $phpErrorLevel;
76 
83  private $tmpFiles = [];
84 
91  private $mwGlobals = [];
92 
98  private $mwGlobalsToUnset = [];
99 
104  private $loggers = [];
105 
109  const DB_PREFIX = 'unittest_';
110  const ORA_DB_PREFIX = 'ut_';
111 
116  protected $supportedDBs = [
117  'mysql',
118  'sqlite',
119  'postgres',
120  'oracle'
121  ];
122 
123  public function __construct( $name = null, array $data = [], $dataName = '' ) {
124  parent::__construct( $name, $data, $dataName );
125 
126  $this->backupGlobals = false;
127  $this->backupStaticAttributes = false;
128  }
129 
130  public function __destruct() {
131  // Complain if self::setUp() was called, but not self::tearDown()
132  // $this->called['setUp'] will be checked by self::testMediaWikiTestCaseParentSetupCalled()
133  if ( isset( $this->called['setUp'] ) && !isset( $this->called['tearDown'] ) ) {
134  throw new MWException( static::class . "::tearDown() must call parent::tearDown()" );
135  }
136  }
137 
138  public static function setUpBeforeClass() {
139  parent::setUpBeforeClass();
140 
141  // Get the service locator, and reset services if it's not done already
142  self::$serviceLocator = self::prepareServices( new GlobalVarConfig() );
143  }
144 
153  public static function getTestUser( $groups = [] ) {
154  return TestUserRegistry::getImmutableTestUser( $groups );
155  }
156 
165  public static function getMutableTestUser( $groups = [] ) {
166  return TestUserRegistry::getMutableTestUser( __CLASS__, $groups );
167  }
168 
177  public static function getTestSysop() {
178  return self::getTestUser( [ 'sysop', 'bureaucrat' ] );
179  }
180 
200  public static function prepareServices( Config $bootstrapConfig ) {
201  static $services = null;
202 
203  if ( !$services ) {
204  $services = self::resetGlobalServices( $bootstrapConfig );
205  }
206  return $services;
207  }
208 
223  protected static function resetGlobalServices( Config $bootstrapConfig = null ) {
224  $oldServices = MediaWikiServices::getInstance();
225  $oldConfigFactory = $oldServices->getConfigFactory();
226  $oldLoadBalancerFactory = $oldServices->getDBLoadBalancerFactory();
227 
228  $testConfig = self::makeTestConfig( $bootstrapConfig );
229 
230  MediaWikiServices::resetGlobalInstance( $testConfig );
231 
232  $serviceLocator = MediaWikiServices::getInstance();
234  $oldConfigFactory,
235  $oldLoadBalancerFactory,
237  );
238  return $serviceLocator;
239  }
240 
250  private static function makeTestConfig(
251  Config $baseConfig = null,
252  Config $customOverrides = null
253  ) {
254  $defaultOverrides = new HashConfig();
255 
256  if ( !$baseConfig ) {
257  $baseConfig = MediaWikiServices::getInstance()->getBootstrapConfig();
258  }
259 
260  /* Some functions require some kind of caching, and will end up using the db,
261  * which we can't allow, as that would open a new connection for mysql.
262  * Replace with a HashBag. They would not be going to persist anyway.
263  */
264  $hashCache = [ 'class' => HashBagOStuff::class, 'reportDupes' => false ];
265  $objectCaches = [
266  CACHE_DB => $hashCache,
267  CACHE_ACCEL => $hashCache,
268  CACHE_MEMCACHED => $hashCache,
269  'apc' => $hashCache,
270  'apcu' => $hashCache,
271  'wincache' => $hashCache,
272  ] + $baseConfig->get( 'ObjectCaches' );
273 
274  $defaultOverrides->set( 'ObjectCaches', $objectCaches );
275  $defaultOverrides->set( 'MainCacheType', CACHE_NONE );
276  $defaultOverrides->set( 'JobTypeConf', [ 'default' => [ 'class' => JobQueueMemory::class ] ] );
277 
278  // Use a fast hash algorithm to hash passwords.
279  $defaultOverrides->set( 'PasswordDefault', 'A' );
280 
281  $testConfig = $customOverrides
282  ? new MultiConfig( [ $customOverrides, $defaultOverrides, $baseConfig ] )
283  : new MultiConfig( [ $defaultOverrides, $baseConfig ] );
284 
285  return $testConfig;
286  }
287 
295  private static function installTestServices(
296  ConfigFactory $oldConfigFactory,
297  LBFactory $oldLoadBalancerFactory,
298  MediaWikiServices $newServices
299  ) {
300  // Use bootstrap config for all configuration.
301  // This allows config overrides via global variables to take effect.
302  $bootstrapConfig = $newServices->getBootstrapConfig();
303  $newServices->resetServiceForTesting( 'ConfigFactory' );
304  $newServices->redefineService(
305  'ConfigFactory',
306  self::makeTestConfigFactoryInstantiator(
307  $oldConfigFactory,
308  [ 'main' => $bootstrapConfig ]
309  )
310  );
311  $newServices->resetServiceForTesting( 'DBLoadBalancerFactory' );
312  $newServices->redefineService(
313  'DBLoadBalancerFactory',
314  function ( MediaWikiServices $services ) use ( $oldLoadBalancerFactory ) {
315  return $oldLoadBalancerFactory;
316  }
317  );
318  }
319 
326  private static function makeTestConfigFactoryInstantiator(
327  ConfigFactory $oldFactory,
328  array $configurations
329  ) {
330  return function ( MediaWikiServices $services ) use ( $oldFactory, $configurations ) {
331  $factory = new ConfigFactory();
332 
333  // clone configurations from $oldFactory that are not overwritten by $configurations
334  $namesToClone = array_diff(
335  $oldFactory->getConfigNames(),
336  array_keys( $configurations )
337  );
338 
339  foreach ( $namesToClone as $name ) {
340  $factory->register( $name, $oldFactory->makeConfig( $name ) );
341  }
342 
343  foreach ( $configurations as $name => $config ) {
344  $factory->register( $name, $config );
345  }
346 
347  return $factory;
348  };
349  }
350 
362  private function doLightweightServiceReset() {
364 
367  $services = MediaWikiServices::getInstance();
368  $services->resetServiceForTesting( 'MainObjectStash' );
369  $services->resetServiceForTesting( 'LocalServerObjectCache' );
370  $services->getMainWANObjectCache()->clearProcessCache();
372 
373  // TODO: move global state into MediaWikiServices
375  if ( session_id() !== '' ) {
376  session_write_close();
377  session_id( '' );
378  }
379 
380  $wgRequest = new FauxRequest();
382  }
383 
384  public function run( PHPUnit_Framework_TestResult $result = null ) {
385  // Reset all caches between tests.
386  $this->doLightweightServiceReset();
387 
388  $needsResetDB = false;
389 
390  if ( !self::$dbSetup || $this->needsDB() ) {
391  // set up a DB connection for this test to use
392 
393  self::$useTemporaryTables = !$this->getCliArg( 'use-normal-tables' );
394  self::$reuseDB = $this->getCliArg( 'reuse-db' );
395 
396  $this->db = wfGetDB( DB_MASTER );
397 
398  $this->checkDbIsSupported();
399 
400  if ( !self::$dbSetup ) {
401  $this->setupAllTestDBs();
402  $this->addCoreDBData();
403 
404  if ( ( $this->db->getType() == 'oracle' || !self::$useTemporaryTables ) && self::$reuseDB ) {
405  $this->resetDB( $this->db, $this->tablesUsed );
406  }
407  }
408 
409  // TODO: the DB setup should be done in setUpBeforeClass(), so the test DB
410  // is available in subclass's setUpBeforeClass() and setUp() methods.
411  // This would also remove the need for the HACK that is oncePerClass().
412  if ( $this->oncePerClass() ) {
413  $this->setUpSchema( $this->db );
414  $this->addDBDataOnce();
415  }
416 
417  $this->addDBData();
418  $needsResetDB = true;
419  }
420 
421  parent::run( $result );
422 
423  if ( $needsResetDB ) {
424  $this->resetDB( $this->db, $this->tablesUsed );
425  }
426  }
427 
431  private function oncePerClass() {
432  // Remember current test class in the database connection,
433  // so we know when we need to run addData.
434 
435  $class = static::class;
436 
437  $first = !isset( $this->db->_hasDataForTestClass )
438  || $this->db->_hasDataForTestClass !== $class;
439 
440  $this->db->_hasDataForTestClass = $class;
441  return $first;
442  }
443 
449  public function usesTemporaryTables() {
451  }
452 
462  protected function getNewTempFile() {
463  $fileName = tempnam( wfTempDir(), 'MW_PHPUnit_' . static::class . '_' );
464  $this->tmpFiles[] = $fileName;
465 
466  return $fileName;
467  }
468 
479  protected function getNewTempDirectory() {
480  // Starting of with a temporary /file/.
481  $fileName = $this->getNewTempFile();
482 
483  // Converting the temporary /file/ to a /directory/
484  // The following is not atomic, but at least we now have a single place,
485  // where temporary directory creation is bundled and can be improved
486  unlink( $fileName );
487  $this->assertTrue( wfMkdirParents( $fileName ) );
488 
489  return $fileName;
490  }
491 
492  protected function setUp() {
493  parent::setUp();
494  $this->called['setUp'] = true;
495 
496  $this->phpErrorLevel = intval( ini_get( 'error_reporting' ) );
497 
498  // Cleaning up temporary files
499  foreach ( $this->tmpFiles as $fileName ) {
500  if ( is_file( $fileName ) || ( is_link( $fileName ) ) ) {
501  unlink( $fileName );
502  } elseif ( is_dir( $fileName ) ) {
503  wfRecursiveRemoveDir( $fileName );
504  }
505  }
506 
507  if ( $this->needsDB() && $this->db ) {
508  // Clean up open transactions
509  while ( $this->db->trxLevel() > 0 ) {
510  $this->db->rollback( __METHOD__, 'flush' );
511  }
512  // Check for unsafe queries
513  if ( $this->db->getType() === 'mysql' ) {
514  $this->db->query( "SET sql_mode = 'STRICT_ALL_TABLES'" );
515  }
516  }
517 
519  ObjectCache::getMainWANInstance()->clearProcessCache();
520 
521  // XXX: reset maintenance triggers
522  // Hook into period lag checks which often happen in long-running scripts
523  $services = MediaWikiServices::getInstance();
524  $lbFactory = $services->getDBLoadBalancerFactory();
525  Maintenance::setLBFactoryTriggers( $lbFactory, $services->getMainConfig() );
526 
527  ob_start( 'MediaWikiTestCase::wfResetOutputBuffersBarrier' );
528  }
529 
530  protected function addTmpFiles( $files ) {
531  $this->tmpFiles = array_merge( $this->tmpFiles, (array)$files );
532  }
533 
534  protected function tearDown() {
536 
537  $status = ob_get_status();
538  if ( isset( $status['name'] ) &&
539  $status['name'] === 'MediaWikiTestCase::wfResetOutputBuffersBarrier'
540  ) {
541  ob_end_flush();
542  }
543 
544  $this->called['tearDown'] = true;
545  // Cleaning up temporary files
546  foreach ( $this->tmpFiles as $fileName ) {
547  if ( is_file( $fileName ) || ( is_link( $fileName ) ) ) {
548  unlink( $fileName );
549  } elseif ( is_dir( $fileName ) ) {
550  wfRecursiveRemoveDir( $fileName );
551  }
552  }
553 
554  if ( $this->needsDB() && $this->db ) {
555  // Clean up open transactions
556  while ( $this->db->trxLevel() > 0 ) {
557  $this->db->rollback( __METHOD__, 'flush' );
558  }
559  if ( $this->db->getType() === 'mysql' ) {
560  $this->db->query( "SET sql_mode = " . $this->db->addQuotes( $wgSQLMode ) );
561  }
562  }
563 
564  // Restore mw globals
565  foreach ( $this->mwGlobals as $key => $value ) {
566  $GLOBALS[$key] = $value;
567  }
568  foreach ( $this->mwGlobalsToUnset as $value ) {
569  unset( $GLOBALS[$value] );
570  }
571  $this->mwGlobals = [];
572  $this->mwGlobalsToUnset = [];
573  $this->restoreLoggers();
574 
575  if ( self::$serviceLocator && MediaWikiServices::getInstance() !== self::$serviceLocator ) {
576  MediaWikiServices::forceGlobalInstance( self::$serviceLocator );
577  }
578 
579  // TODO: move global state into MediaWikiServices
581  if ( session_id() !== '' ) {
582  session_write_close();
583  session_id( '' );
584  }
585  $wgRequest = new FauxRequest();
588 
589  $phpErrorLevel = intval( ini_get( 'error_reporting' ) );
590 
591  if ( $phpErrorLevel !== $this->phpErrorLevel ) {
592  ini_set( 'error_reporting', $this->phpErrorLevel );
593 
594  $oldHex = strtoupper( dechex( $this->phpErrorLevel ) );
595  $newHex = strtoupper( dechex( $phpErrorLevel ) );
596  $message = "PHP error_reporting setting was left dirty: "
597  . "was 0x$oldHex before test, 0x$newHex after test!";
598 
599  $this->fail( $message );
600  }
601 
602  parent::tearDown();
603  }
604 
613  final public function testMediaWikiTestCaseParentSetupCalled() {
614  $this->assertArrayHasKey( 'setUp', $this->called,
615  static::class . '::setUp() must call parent::setUp()'
616  );
617  }
618 
628  protected function setService( $name, $object ) {
629  // If we did not yet override the service locator, so so now.
630  if ( MediaWikiServices::getInstance() === self::$serviceLocator ) {
631  $this->overrideMwServices();
632  }
633 
634  MediaWikiServices::getInstance()->disableService( $name );
635  MediaWikiServices::getInstance()->redefineService(
636  $name,
637  function () use ( $object ) {
638  return $object;
639  }
640  );
641  }
642 
678  protected function setMwGlobals( $pairs, $value = null ) {
679  if ( is_string( $pairs ) ) {
680  $pairs = [ $pairs => $value ];
681  }
682 
683  $this->stashMwGlobals( array_keys( $pairs ) );
684 
685  foreach ( $pairs as $key => $value ) {
686  $GLOBALS[$key] = $value;
687  }
688  }
689 
698  private static function canShallowCopy( $value ) {
699  if ( is_scalar( $value ) || $value === null ) {
700  return true;
701  }
702  if ( is_array( $value ) ) {
703  foreach ( $value as $subValue ) {
704  if ( !is_scalar( $subValue ) && $subValue !== null ) {
705  return false;
706  }
707  }
708  return true;
709  }
710  return false;
711  }
712 
730  protected function stashMwGlobals( $globalKeys ) {
731  if ( is_string( $globalKeys ) ) {
732  $globalKeys = [ $globalKeys ];
733  }
734 
735  foreach ( $globalKeys as $globalKey ) {
736  // NOTE: make sure we only save the global once or a second call to
737  // setMwGlobals() on the same global would override the original
738  // value.
739  if (
740  !array_key_exists( $globalKey, $this->mwGlobals ) &&
741  !array_key_exists( $globalKey, $this->mwGlobalsToUnset )
742  ) {
743  if ( !array_key_exists( $globalKey, $GLOBALS ) ) {
744  $this->mwGlobalsToUnset[$globalKey] = $globalKey;
745  continue;
746  }
747  // NOTE: we serialize then unserialize the value in case it is an object
748  // this stops any objects being passed by reference. We could use clone
749  // and if is_object but this does account for objects within objects!
750  if ( self::canShallowCopy( $GLOBALS[$globalKey] ) ) {
751  $this->mwGlobals[$globalKey] = $GLOBALS[$globalKey];
752  } elseif (
753  // Many MediaWiki types are safe to clone. These are the
754  // ones that are most commonly stashed.
755  $GLOBALS[$globalKey] instanceof Language ||
756  $GLOBALS[$globalKey] instanceof User ||
757  $GLOBALS[$globalKey] instanceof FauxRequest
758  ) {
759  $this->mwGlobals[$globalKey] = clone $GLOBALS[$globalKey];
760  } elseif ( $this->containsClosure( $GLOBALS[$globalKey] ) ) {
761  // Serializing Closure only gives a warning on HHVM while
762  // it throws an Exception on Zend.
763  // Workaround for https://github.com/facebook/hhvm/issues/6206
764  $this->mwGlobals[$globalKey] = $GLOBALS[$globalKey];
765  } else {
766  try {
767  $this->mwGlobals[$globalKey] = unserialize( serialize( $GLOBALS[$globalKey] ) );
768  } catch ( Exception $e ) {
769  $this->mwGlobals[$globalKey] = $GLOBALS[$globalKey];
770  }
771  }
772  }
773  }
774  }
775 
782  private function containsClosure( $var, $maxDepth = 15 ) {
783  if ( $var instanceof Closure ) {
784  return true;
785  }
786  if ( !is_array( $var ) || $maxDepth === 0 ) {
787  return false;
788  }
789 
790  foreach ( $var as $value ) {
791  if ( $this->containsClosure( $value, $maxDepth - 1 ) ) {
792  return true;
793  }
794  }
795  return false;
796  }
797 
813  protected function mergeMwGlobalArrayValue( $name, $values ) {
814  if ( !isset( $GLOBALS[$name] ) ) {
815  $merged = $values;
816  } else {
817  if ( !is_array( $GLOBALS[$name] ) ) {
818  throw new MWException( "MW global $name is not an array." );
819  }
820 
821  // NOTE: do not use array_merge, it screws up for numeric keys.
822  $merged = $GLOBALS[$name];
823  foreach ( $values as $k => $v ) {
824  $merged[$k] = $v;
825  }
826  }
827 
828  $this->setMwGlobals( $name, $merged );
829  }
830 
845  protected function overrideMwServices( Config $configOverrides = null, array $services = [] ) {
846  if ( !$configOverrides ) {
847  $configOverrides = new HashConfig();
848  }
849 
850  $oldInstance = MediaWikiServices::getInstance();
851  $oldConfigFactory = $oldInstance->getConfigFactory();
852  $oldLoadBalancerFactory = $oldInstance->getDBLoadBalancerFactory();
853 
854  $testConfig = self::makeTestConfig( null, $configOverrides );
855  $newInstance = new MediaWikiServices( $testConfig );
856 
857  // Load the default wiring from the specified files.
858  // NOTE: this logic mirrors the logic in MediaWikiServices::newInstance.
859  $wiringFiles = $testConfig->get( 'ServiceWiringFiles' );
860  $newInstance->loadWiringFiles( $wiringFiles );
861 
862  // Provide a traditional hook point to allow extensions to configure services.
863  Hooks::run( 'MediaWikiServices', [ $newInstance ] );
864 
865  foreach ( $services as $name => $callback ) {
866  $newInstance->redefineService( $name, $callback );
867  }
868 
870  $oldConfigFactory,
871  $oldLoadBalancerFactory,
872  $newInstance
873  );
874  MediaWikiServices::forceGlobalInstance( $newInstance );
875 
876  return $newInstance;
877  }
878 
883  public function setUserLang( $lang ) {
884  RequestContext::getMain()->setLanguage( $lang );
885  $this->setMwGlobals( 'wgLang', RequestContext::getMain()->getLanguage() );
886  }
887 
892  public function setContentLang( $lang ) {
893  if ( $lang instanceof Language ) {
894  $langCode = $lang->getCode();
895  $langObj = $lang;
896  } else {
897  $langCode = $lang;
898  $langObj = Language::factory( $langCode );
899  }
900  $this->setMwGlobals( [
901  'wgLanguageCode' => $langCode,
902  'wgContLang' => $langObj,
903  ] );
904  }
905 
920  public function setGroupPermissions( $newPerms, $newKey = null, $newValue = null ) {
922 
923  $this->stashMwGlobals( 'wgGroupPermissions' );
924 
925  if ( is_string( $newPerms ) ) {
926  $newPerms = [ $newPerms => [ $newKey => $newValue ] ];
927  }
928 
929  foreach ( $newPerms as $group => $permissions ) {
930  foreach ( $permissions as $key => $value ) {
931  $wgGroupPermissions[$group][$key] = $value;
932  }
933  }
934  }
935 
942  protected function setLogger( $channel, LoggerInterface $logger ) {
943  // TODO: Once loggers are managed by MediaWikiServices, use
944  // overrideMwServices() to set loggers.
945 
946  $provider = LoggerFactory::getProvider();
947  $wrappedProvider = TestingAccessWrapper::newFromObject( $provider );
948  $singletons = $wrappedProvider->singletons;
949  if ( $provider instanceof MonologSpi ) {
950  if ( !isset( $this->loggers[$channel] ) ) {
951  $this->loggers[$channel] = isset( $singletons['loggers'][$channel] )
952  ? $singletons['loggers'][$channel] : null;
953  }
954  $singletons['loggers'][$channel] = $logger;
955  } elseif ( $provider instanceof LegacySpi ) {
956  if ( !isset( $this->loggers[$channel] ) ) {
957  $this->loggers[$channel] = isset( $singletons[$channel] ) ? $singletons[$channel] : null;
958  }
959  $singletons[$channel] = $logger;
960  } else {
961  throw new LogicException( __METHOD__ . ': setting a logger for ' . get_class( $provider )
962  . ' is not implemented' );
963  }
964  $wrappedProvider->singletons = $singletons;
965  }
966 
971  private function restoreLoggers() {
972  $provider = LoggerFactory::getProvider();
973  $wrappedProvider = TestingAccessWrapper::newFromObject( $provider );
974  $singletons = $wrappedProvider->singletons;
975  foreach ( $this->loggers as $channel => $logger ) {
976  if ( $provider instanceof MonologSpi ) {
977  if ( $logger === null ) {
978  unset( $singletons['loggers'][$channel] );
979  } else {
980  $singletons['loggers'][$channel] = $logger;
981  }
982  } elseif ( $provider instanceof LegacySpi ) {
983  if ( $logger === null ) {
984  unset( $singletons[$channel] );
985  } else {
986  $singletons[$channel] = $logger;
987  }
988  }
989  }
990  $wrappedProvider->singletons = $singletons;
991  $this->loggers = [];
992  }
993 
998  public function dbPrefix() {
999  return $this->db->getType() == 'oracle' ? self::ORA_DB_PREFIX : self::DB_PREFIX;
1000  }
1001 
1006  public function needsDB() {
1007  // If the test says it uses database tables, it needs the database
1008  if ( $this->tablesUsed ) {
1009  return true;
1010  }
1011 
1012  // If the test class says it belongs to the Database group, it needs the database.
1013  // NOTE: This ONLY checks for the group in the class level doc comment.
1014  $rc = new ReflectionClass( $this );
1015  if ( preg_match( '/@group +Database/im', $rc->getDocComment() ) ) {
1016  return true;
1017  }
1018 
1019  return false;
1020  }
1021 
1033  protected function insertPage(
1034  $pageName,
1035  $text = 'Sample page for unit test.',
1036  $namespace = null
1037  ) {
1038  if ( is_string( $pageName ) ) {
1039  $title = Title::newFromText( $pageName, $namespace );
1040  } else {
1041  $title = $pageName;
1042  }
1043 
1044  $user = static::getTestSysop()->getUser();
1045  $comment = __METHOD__ . ': Sample page for unit test.';
1046 
1047  $page = WikiPage::factory( $title );
1048  $page->doEditContent( ContentHandler::makeContent( $text, $title ), $comment, 0, false, $user );
1049 
1050  return [
1051  'title' => $title,
1052  'id' => $page->getId(),
1053  ];
1054  }
1055 
1071  public function addDBDataOnce() {
1072  }
1073 
1083  public function addDBData() {
1084  }
1085 
1086  private function addCoreDBData() {
1087  if ( $this->db->getType() == 'oracle' ) {
1088  # Insert 0 user to prevent FK violations
1089  # Anonymous user
1090  if ( !$this->db->selectField( 'user', '1', [ 'user_id' => 0 ] ) ) {
1091  $this->db->insert( 'user', [
1092  'user_id' => 0,
1093  'user_name' => 'Anonymous' ], __METHOD__, [ 'IGNORE' ] );
1094  }
1095 
1096  # Insert 0 page to prevent FK violations
1097  # Blank page
1098  if ( !$this->db->selectField( 'page', '1', [ 'page_id' => 0 ] ) ) {
1099  $this->db->insert( 'page', [
1100  'page_id' => 0,
1101  'page_namespace' => 0,
1102  'page_title' => ' ',
1103  'page_restrictions' => null,
1104  'page_is_redirect' => 0,
1105  'page_is_new' => 0,
1106  'page_random' => 0,
1107  'page_touched' => $this->db->timestamp(),
1108  'page_latest' => 0,
1109  'page_len' => 0 ], __METHOD__, [ 'IGNORE' ] );
1110  }
1111  }
1112 
1114 
1116 
1117  // Make sysop user
1118  $user = static::getTestSysop()->getUser();
1119 
1120  // Make 1 page with 1 revision
1121  $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
1122  if ( $page->getId() == 0 ) {
1123  $page->doEditContent(
1124  new WikitextContent( 'UTContent' ),
1125  'UTPageSummary',
1127  false,
1128  $user
1129  );
1130  // an edit always attempt to purge backlink links such as history
1131  // pages. That is unneccessary.
1132  JobQueueGroup::singleton()->get( 'htmlCacheUpdate' )->delete();
1133  // WikiPages::doEditUpdates randomly adds RC purges
1134  JobQueueGroup::singleton()->get( 'recentChangesUpdate' )->delete();
1135 
1136  // doEditContent() probably started the session via
1137  // User::loadFromSession(). Close it now.
1138  if ( session_id() !== '' ) {
1139  session_write_close();
1140  session_id( '' );
1141  }
1142  }
1143  }
1144 
1152  public static function teardownTestDB() {
1154 
1155  if ( !self::$dbSetup ) {
1156  return;
1157  }
1158 
1159  Hooks::run( 'UnitTestsBeforeDatabaseTeardown' );
1160 
1161  foreach ( $wgJobClasses as $type => $class ) {
1162  // Delete any jobs under the clone DB (or old prefix in other stores)
1163  JobQueueGroup::singleton()->get( $type )->delete();
1164  }
1165 
1166  CloneDatabase::changePrefix( self::$oldTablePrefix );
1167 
1168  self::$oldTablePrefix = false;
1169  self::$dbSetup = false;
1170  }
1171 
1185  protected static function setupDatabaseWithTestPrefix( IMaintainableDatabase $db, $prefix ) {
1186  $tablesCloned = self::listTables( $db );
1187  $dbClone = new CloneDatabase( $db, $tablesCloned, $prefix );
1188  $dbClone->useTemporaryTables( self::$useTemporaryTables );
1189 
1190  $db->_originalTablePrefix = $db->tablePrefix();
1191 
1192  if ( ( $db->getType() == 'oracle' || !self::$useTemporaryTables ) && self::$reuseDB ) {
1193  CloneDatabase::changePrefix( $prefix );
1194 
1195  return false;
1196  } else {
1197  $dbClone->cloneTableStructure();
1198  return true;
1199  }
1200  }
1201 
1205  public function setupAllTestDBs() {
1207 
1208  self::$oldTablePrefix = $wgDBprefix;
1209 
1210  $testPrefix = $this->dbPrefix();
1211 
1212  // switch to a temporary clone of the database
1213  self::setupTestDB( $this->db, $testPrefix );
1214 
1215  if ( self::isUsingExternalStoreDB() ) {
1216  self::setupExternalStoreTestDBs( $testPrefix );
1217  }
1218  }
1219 
1241  public static function setupTestDB( Database $db, $prefix ) {
1242  if ( self::$dbSetup ) {
1243  return;
1244  }
1245 
1246  if ( $db->tablePrefix() === $prefix ) {
1247  throw new MWException(
1248  'Cannot run unit tests, the database prefix is already "' . $prefix . '"' );
1249  }
1250 
1251  // TODO: the below should be re-written as soon as LBFactory, LoadBalancer,
1252  // and Database no longer use global state.
1253 
1254  self::$dbSetup = true;
1255 
1256  if ( !self::setupDatabaseWithTestPrefix( $db, $prefix ) ) {
1257  return;
1258  }
1259 
1260  // Assuming this isn't needed for External Store database, and not sure if the procedure
1261  // would be available there.
1262  if ( $db->getType() == 'oracle' ) {
1263  $db->query( 'BEGIN FILL_WIKI_INFO; END;' );
1264  }
1265 
1266  Hooks::run( 'UnitTestsAfterDatabaseSetup', [ $db, $prefix ] );
1267  }
1268 
1274  protected static function setupExternalStoreTestDBs( $testPrefix ) {
1276  foreach ( $connections as $dbw ) {
1277  // Hack: cloneTableStructure sets $wgDBprefix to the unit test
1278  // prefix,. Even though listTables now uses tablePrefix, that
1279  // itself is populated from $wgDBprefix by default.
1280 
1281  // We have to set it back, or we won't find the original 'blobs'
1282  // table to copy.
1283 
1284  $dbw->tablePrefix( self::$oldTablePrefix );
1285  self::setupDatabaseWithTestPrefix( $dbw, $testPrefix );
1286  }
1287  }
1288 
1295  protected static function getExternalStoreDatabaseConnections() {
1297 
1299  $externalStoreDB = ExternalStore::getStoreObject( 'DB' );
1300  $defaultArray = (array)$wgDefaultExternalStore;
1301  $dbws = [];
1302  foreach ( $defaultArray as $url ) {
1303  if ( strpos( $url, 'DB://' ) === 0 ) {
1304  list( $proto, $cluster ) = explode( '://', $url, 2 );
1305  // Avoid getMaster() because setupDatabaseWithTestPrefix()
1306  // requires Database instead of plain DBConnRef/IDatabase
1307  $dbws[] = $externalStoreDB->getMaster( $cluster );
1308  }
1309  }
1310 
1311  return $dbws;
1312  }
1313 
1319  protected static function isUsingExternalStoreDB() {
1321  if ( !$wgDefaultExternalStore ) {
1322  return false;
1323  }
1324 
1325  $defaultArray = (array)$wgDefaultExternalStore;
1326  foreach ( $defaultArray as $url ) {
1327  if ( strpos( $url, 'DB://' ) === 0 ) {
1328  return true;
1329  }
1330  }
1331 
1332  return false;
1333  }
1334 
1340  if ( $db->tablePrefix() !== $this->dbPrefix() ) {
1341  throw new LogicException(
1342  'Trying to delete mock tables, but table prefix does not indicate a mock database.'
1343  );
1344  }
1345  }
1346 
1347  private static $schemaOverrideDefaults = [
1348  'scripts' => [],
1349  'create' => [],
1350  'drop' => [],
1351  'alter' => [],
1352  ];
1353 
1369  return [];
1370  }
1371 
1379  private function undoSchemaOverrides( IMaintainableDatabase $db, $oldOverrides ) {
1380  $this->ensureMockDatabaseConnection( $db );
1381 
1382  $oldOverrides = $oldOverrides + self::$schemaOverrideDefaults;
1383  $originalTables = $this->listOriginalTables( $db );
1384 
1385  // Drop tables that need to be restored or removed.
1386  $tablesToDrop = array_merge( $oldOverrides['create'], $oldOverrides['alter'] );
1387 
1388  // Restore tables that have been dropped or created or altered,
1389  // if they exist in the original schema.
1390  $tablesToRestore = array_merge( $tablesToDrop, $oldOverrides['drop'] );
1391  $tablesToRestore = array_intersect( $originalTables, $tablesToRestore );
1392 
1393  if ( $tablesToDrop ) {
1394  $this->dropMockTables( $db, $tablesToDrop );
1395  }
1396 
1397  if ( $tablesToRestore ) {
1398  $this->recloneMockTables( $db, $tablesToRestore );
1399  }
1400  }
1401 
1407  private function setUpSchema( IMaintainableDatabase $db ) {
1408  // Undo any active overrides.
1409  $oldOverrides = isset( $db->_schemaOverrides ) ? $db->_schemaOverrides
1411 
1412  if ( $oldOverrides['alter'] || $oldOverrides['create'] || $oldOverrides['drop'] ) {
1413  $this->undoSchemaOverrides( $db, $oldOverrides );
1414  }
1415 
1416  // Determine new overrides.
1417  $overrides = $this->getSchemaOverrides( $db ) + self::$schemaOverrideDefaults;
1418 
1419  $extraKeys = array_diff(
1420  array_keys( $overrides ),
1421  array_keys( self::$schemaOverrideDefaults )
1422  );
1423 
1424  if ( $extraKeys ) {
1425  throw new InvalidArgumentException(
1426  'Schema override contains extra keys: ' . var_export( $extraKeys, true )
1427  );
1428  }
1429 
1430  if ( !$overrides['scripts'] ) {
1431  // no scripts to run
1432  return;
1433  }
1434 
1435  if ( !$overrides['create'] && !$overrides['drop'] && !$overrides['alter'] ) {
1436  throw new InvalidArgumentException(
1437  'Schema override scripts given, but no tables are declared to be '
1438  . 'created, dropped or altered.'
1439  );
1440  }
1441 
1442  $this->ensureMockDatabaseConnection( $db );
1443 
1444  // Drop the tables that will be created by the schema scripts.
1445  $originalTables = $this->listOriginalTables( $db );
1446  $tablesToDrop = array_intersect( $originalTables, $overrides['create'] );
1447 
1448  if ( $tablesToDrop ) {
1449  $this->dropMockTables( $db, $tablesToDrop );
1450  }
1451 
1452  // Run schema override scripts.
1453  foreach ( $overrides['scripts'] as $script ) {
1454  $db->sourceFile(
1455  $script,
1456  null,
1457  null,
1458  __METHOD__,
1459  function ( $cmd ) {
1460  return $this->mungeSchemaUpdateQuery( $cmd );
1461  }
1462  );
1463  }
1464 
1465  $db->_schemaOverrides = $overrides;
1466  }
1467 
1468  private function mungeSchemaUpdateQuery( $cmd ) {
1469  return self::$useTemporaryTables
1470  ? preg_replace( '/\bCREATE\s+TABLE\b/i', 'CREATE TEMPORARY TABLE', $cmd )
1471  : $cmd;
1472  }
1473 
1481  $this->ensureMockDatabaseConnection( $db );
1482 
1483  foreach ( $tables as $tbl ) {
1484  $tbl = $db->tableName( $tbl );
1485  $db->query( "DROP TABLE IF EXISTS $tbl", __METHOD__ );
1486 
1487  if ( $tbl === 'page' ) {
1488  // Forget about the pages since they don't
1489  // exist in the DB.
1490  LinkCache::singleton()->clear();
1491  }
1492  }
1493  }
1494 
1502  if ( !isset( $db->_originalTablePrefix ) ) {
1503  throw new LogicException( 'No original table prefix know, cannot list tables!' );
1504  }
1505 
1506  $originalTables = $db->listTables( $db->_originalTablePrefix, __METHOD__ );
1507  return $originalTables;
1508  }
1509 
1519  $this->ensureMockDatabaseConnection( $db );
1520 
1521  if ( !isset( $db->_originalTablePrefix ) ) {
1522  throw new LogicException( 'No original table prefix know, cannot restore tables!' );
1523  }
1524 
1525  $originalTables = $this->listOriginalTables( $db );
1526  $tables = array_intersect( $tables, $originalTables );
1527 
1528  $dbClone = new CloneDatabase( $db, $tables, $db->tablePrefix(), $db->_originalTablePrefix );
1529  $dbClone->useTemporaryTables( self::$useTemporaryTables );
1530 
1531  $dbClone->cloneTableStructure();
1532  }
1533 
1540  private function resetDB( $db, $tablesUsed ) {
1541  if ( $db ) {
1542  $userTables = [ 'user', 'user_groups', 'user_properties', 'actor' ];
1543  $pageTables = [ 'page', 'revision', 'ip_changes', 'revision_comment_temp',
1544  'revision_actor_temp', 'comment' ];
1545  $coreDBDataTables = array_merge( $userTables, $pageTables );
1546 
1547  // If any of the user or page tables were marked as used, we should clear all of them.
1548  if ( array_intersect( $tablesUsed, $userTables ) ) {
1549  $tablesUsed = array_unique( array_merge( $tablesUsed, $userTables ) );
1551  }
1552  if ( array_intersect( $tablesUsed, $pageTables ) ) {
1553  $tablesUsed = array_unique( array_merge( $tablesUsed, $pageTables ) );
1554  }
1555 
1556  $truncate = in_array( $db->getType(), [ 'oracle', 'mysql' ] );
1557  foreach ( $tablesUsed as $tbl ) {
1558  // TODO: reset interwiki table to its original content.
1559  if ( $tbl == 'interwiki' ) {
1560  continue;
1561  }
1562 
1563  if ( !$db->tableExists( $tbl ) ) {
1564  continue;
1565  }
1566 
1567  if ( $truncate ) {
1568  $db->query( 'TRUNCATE TABLE ' . $db->tableName( $tbl ), __METHOD__ );
1569  } else {
1570  $db->delete( $tbl, '*', __METHOD__ );
1571  }
1572 
1573  if ( in_array( $db->getType(), [ 'postgres', 'sqlite' ], true ) ) {
1574  // Reset the table's sequence too.
1575  $db->resetSequenceForTable( $tbl, __METHOD__ );
1576  }
1577 
1578  if ( $tbl === 'page' ) {
1579  // Forget about the pages since they don't
1580  // exist in the DB.
1581  LinkCache::singleton()->clear();
1582  }
1583  }
1584 
1585  if ( array_intersect( $tablesUsed, $coreDBDataTables ) ) {
1586  // Re-add core DB data that was deleted
1587  $this->addCoreDBData();
1588  }
1589  }
1590  }
1591 
1592  private static function unprefixTable( &$tableName, $ind, $prefix ) {
1593  $tableName = substr( $tableName, strlen( $prefix ) );
1594  }
1595 
1596  private static function isNotUnittest( $table ) {
1597  return strpos( $table, self::DB_PREFIX ) !== 0;
1598  }
1599 
1607  public static function listTables( IMaintainableDatabase $db ) {
1608  $prefix = $db->tablePrefix();
1609  $tables = $db->listTables( $prefix, __METHOD__ );
1610 
1611  if ( $db->getType() === 'mysql' ) {
1612  static $viewListCache = null;
1613  if ( $viewListCache === null ) {
1614  $viewListCache = $db->listViews( null, __METHOD__ );
1615  }
1616  // T45571: cannot clone VIEWs under MySQL
1617  $tables = array_diff( $tables, $viewListCache );
1618  }
1619  array_walk( $tables, [ __CLASS__, 'unprefixTable' ], $prefix );
1620 
1621  // Don't duplicate test tables from the previous fataled run
1622  $tables = array_filter( $tables, [ __CLASS__, 'isNotUnittest' ] );
1623 
1624  if ( $db->getType() == 'sqlite' ) {
1625  $tables = array_flip( $tables );
1626  // these are subtables of searchindex and don't need to be duped/dropped separately
1627  unset( $tables['searchindex_content'] );
1628  unset( $tables['searchindex_segdir'] );
1629  unset( $tables['searchindex_segments'] );
1630  $tables = array_flip( $tables );
1631  }
1632 
1633  return $tables;
1634  }
1635 
1640  protected function checkDbIsSupported() {
1641  if ( !in_array( $this->db->getType(), $this->supportedDBs ) ) {
1642  throw new MWException( $this->db->getType() . " is not currently supported for unit testing." );
1643  }
1644  }
1645 
1651  public function getCliArg( $offset ) {
1652  if ( isset( PHPUnitMaintClass::$additionalOptions[$offset] ) ) {
1653  return PHPUnitMaintClass::$additionalOptions[$offset];
1654  }
1655 
1656  return null;
1657  }
1658 
1664  public function setCliArg( $offset, $value ) {
1666  }
1667 
1675  public function hideDeprecated( $function ) {
1676  Wikimedia\suppressWarnings();
1677  wfDeprecated( $function );
1678  Wikimedia\restoreWarnings();
1679  }
1680 
1701  protected function assertSelect(
1702  $table, $fields, $condition, array $expectedRows, array $options = [], array $join_conds = []
1703  ) {
1704  if ( !$this->needsDB() ) {
1705  throw new MWException( 'When testing database state, the test cases\'s needDB()' .
1706  ' method should return true. Use @group Database or $this->tablesUsed.' );
1707  }
1708 
1709  $db = wfGetDB( DB_REPLICA );
1710 
1711  $res = $db->select(
1712  $table,
1713  $fields,
1714  $condition,
1715  wfGetCaller(),
1716  $options + [ 'ORDER BY' => $fields ],
1717  $join_conds
1718  );
1719  $this->assertNotEmpty( $res, "query failed: " . $db->lastError() );
1720 
1721  $i = 0;
1722 
1723  foreach ( $expectedRows as $expected ) {
1724  $r = $res->fetchRow();
1725  self::stripStringKeys( $r );
1726 
1727  $i += 1;
1728  $this->assertNotEmpty( $r, "row #$i missing" );
1729 
1730  $this->assertEquals( $expected, $r, "row #$i mismatches" );
1731  }
1732 
1733  $r = $res->fetchRow();
1734  self::stripStringKeys( $r );
1735 
1736  $this->assertFalse( $r, "found extra row (after #$i)" );
1737  }
1738 
1750  protected function arrayWrap( array $elements ) {
1751  return array_map(
1752  function ( $element ) {
1753  return [ $element ];
1754  },
1755  $elements
1756  );
1757  }
1758 
1771  protected function assertArrayEquals( array $expected, array $actual,
1772  $ordered = false, $named = false
1773  ) {
1774  if ( !$ordered ) {
1775  $this->objectAssociativeSort( $expected );
1776  $this->objectAssociativeSort( $actual );
1777  }
1778 
1779  if ( !$named ) {
1780  $expected = array_values( $expected );
1781  $actual = array_values( $actual );
1782  }
1783 
1784  call_user_func_array(
1785  [ $this, 'assertEquals' ],
1786  array_merge( [ $expected, $actual ], array_slice( func_get_args(), 4 ) )
1787  );
1788  }
1789 
1802  protected function assertHTMLEquals( $expected, $actual, $msg = '' ) {
1803  $expected = str_replace( '>', ">\n", $expected );
1804  $actual = str_replace( '>', ">\n", $actual );
1805 
1806  $this->assertEquals( $expected, $actual, $msg );
1807  }
1808 
1816  protected function objectAssociativeSort( array &$array ) {
1817  uasort(
1818  $array,
1819  function ( $a, $b ) {
1820  return serialize( $a ) > serialize( $b ) ? 1 : -1;
1821  }
1822  );
1823  }
1824 
1834  protected static function stripStringKeys( &$r ) {
1835  if ( !is_array( $r ) ) {
1836  return;
1837  }
1838 
1839  foreach ( $r as $k => $v ) {
1840  if ( is_string( $k ) ) {
1841  unset( $r[$k] );
1842  }
1843  }
1844  }
1845 
1859  protected function assertTypeOrValue( $type, $actual, $value = false, $message = '' ) {
1860  if ( $actual === $value ) {
1861  $this->assertTrue( true, $message );
1862  } else {
1863  $this->assertType( $type, $actual, $message );
1864  }
1865  }
1866 
1878  protected function assertType( $type, $actual, $message = '' ) {
1879  if ( class_exists( $type ) || interface_exists( $type ) ) {
1880  $this->assertInstanceOf( $type, $actual, $message );
1881  } else {
1882  $this->assertInternalType( $type, $actual, $message );
1883  }
1884  }
1885 
1895  protected function isWikitextNS( $ns ) {
1897 
1898  if ( isset( $wgNamespaceContentModels[$ns] ) ) {
1900  }
1901 
1902  return true;
1903  }
1904 
1912  protected function getDefaultWikitextNS() {
1914 
1915  static $wikitextNS = null; // this is not going to change
1916  if ( $wikitextNS !== null ) {
1917  return $wikitextNS;
1918  }
1919 
1920  // quickly short out on most common case:
1921  if ( !isset( $wgNamespaceContentModels[NS_MAIN] ) ) {
1922  return NS_MAIN;
1923  }
1924 
1925  // NOTE: prefer content namespaces
1926  $namespaces = array_unique( array_merge(
1928  [ NS_MAIN, NS_HELP, NS_PROJECT ], // prefer these
1930  ) );
1931 
1932  $namespaces = array_diff( $namespaces, [
1933  NS_FILE, NS_CATEGORY, NS_MEDIAWIKI, NS_USER // don't mess with magic namespaces
1934  ] );
1935 
1936  $talk = array_filter( $namespaces, function ( $ns ) {
1937  return MWNamespace::isTalk( $ns );
1938  } );
1939 
1940  // prefer non-talk pages
1941  $namespaces = array_diff( $namespaces, $talk );
1942  $namespaces = array_merge( $namespaces, $talk );
1943 
1944  // check default content model of each namespace
1945  foreach ( $namespaces as $ns ) {
1946  if ( !isset( $wgNamespaceContentModels[$ns] ) ||
1948  ) {
1949  $wikitextNS = $ns;
1950 
1951  return $wikitextNS;
1952  }
1953  }
1954 
1955  // give up
1956  // @todo Inside a test, we could skip the test as incomplete.
1957  // But frequently, this is used in fixture setup.
1958  throw new MWException( "No namespace defaults to wikitext!" );
1959  }
1960 
1967  protected function markTestSkippedIfNoDiff3() {
1968  global $wgDiff3;
1969 
1970  # This check may also protect against code injection in
1971  # case of broken installations.
1972  Wikimedia\suppressWarnings();
1973  $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
1974  Wikimedia\restoreWarnings();
1975 
1976  if ( !$haveDiff3 ) {
1977  $this->markTestSkipped( "Skip test, since diff3 is not configured" );
1978  }
1979  }
1980 
1989  protected function checkPHPExtension( $extName ) {
1990  $loaded = extension_loaded( $extName );
1991  if ( !$loaded ) {
1992  $this->markTestSkipped( "PHP extension '$extName' is not loaded, skipping." );
1993  }
1994 
1995  return $loaded;
1996  }
1997 
2003  public static function wfResetOutputBuffersBarrier( $buffer ) {
2004  return $buffer;
2005  }
2006 
2014  protected function setTemporaryHook( $hookName, $handler ) {
2015  $this->mergeMwGlobalArrayValue( 'wgHooks', [ $hookName => [ $handler ] ] );
2016  }
2017 
2027  protected function assertFileContains(
2028  $fileName,
2029  $actualData,
2030  $createIfMissing = true,
2031  $msg = ''
2032  ) {
2033  if ( $createIfMissing ) {
2034  if ( !file_exists( $fileName ) ) {
2035  file_put_contents( $fileName, $actualData );
2036  $this->markTestSkipped( 'Data file $fileName does not exist' );
2037  }
2038  } else {
2039  self::assertFileExists( $fileName );
2040  }
2041  self::assertEquals( file_get_contents( $fileName ), $actualData, $msg );
2042  }
2043 }
Wikimedia\Rdbms\Database\tablePrefix
tablePrefix( $prefix=null)
Get/set the table prefix.
Definition: Database.php:593
ObjectCache\clear
static clear()
Clear all the cached instances.
Definition: ObjectCache.php:410
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:247
MediaWiki\Logger\LegacySpi
LoggerFactory service provider that creates LegacyLogger instances.
Definition: LegacySpi.php:37
MediaWikiTestCase\$reuseDB
static $reuseDB
Definition: MediaWikiTestCase.php:66
MediaWiki\Logger\MonologSpi
LoggerFactory service provider that creates loggers implemented by Monolog.
Definition: MonologSpi.php:115
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:48
$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 probably a stub 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:903
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:8554
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:273
MediaWikiTestCase\assertArrayEquals
assertArrayEquals(array $expected, array $actual, $ordered=false, $named=false)
Assert that two arrays are equal.
Definition: MediaWikiTestCase.php:1771
MediaWikiTestCase\stashMwGlobals
stashMwGlobals( $globalKeys)
Stashes the global, will be restored in tearDown()
Definition: MediaWikiTestCase.php:730
NS_HELP
const NS_HELP
Definition: Defines.php:86
MWNamespace\getValidNamespaces
static getValidNamespaces()
Returns an array of the namespaces (by integer id) that exist on the wiki.
Definition: MWNamespace.php:290
use
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
Definition: APACHE-LICENSE-2.0.txt:10
Wikimedia\Rdbms\Database\listTables
listTables( $prefix=null, $fname=__METHOD__)
List all tables on the database.
Definition: Database.php:3898
MWNamespace\isTalk
static isTalk( $index)
Is the given namespace a talk namespace?
Definition: MWNamespace.php:118
MediaWikiTestCase\mergeMwGlobalArrayValue
mergeMwGlobalArrayValue( $name, $values)
Merges the given values into a MW global array variable.
Definition: MediaWikiTestCase.php:813
MediaWikiTestCase\getSchemaOverrides
getSchemaOverrides(IMaintainableDatabase $db)
Stub.
Definition: MediaWikiTestCase.php:1368
MediaWikiTestCase\restoreLoggers
restoreLoggers()
Restores loggers replaced by setLogger().
Definition: MediaWikiTestCase.php:971
MediaWikiTestCase\getTestUser
static getTestUser( $groups=[])
Convenience method for getting an immutable test user.
Definition: MediaWikiTestCase.php:153
MediaWikiTestCase\wfResetOutputBuffersBarrier
static wfResetOutputBuffersBarrier( $buffer)
Used as a marker to prevent wfResetOutputBuffers from breaking PHPUnit.
Definition: MediaWikiTestCase.php:2003
MediaWikiTestCase\stripStringKeys
static stripStringKeys(&$r)
Utility function for eliminating all string keys from an array.
Definition: MediaWikiTestCase.php:1834
MultiConfig
Provides a fallback sequence for Config objects.
Definition: MultiConfig.php:28
array
the array() calling protocol came about after MediaWiki 1.4rc1.
wfMkdirParents
wfMkdirParents( $dir, $mode=null, $caller=null)
Make directory, and make all parent directories if they don't exist.
Definition: GlobalFunctions.php:2076
MediaWikiTestCase\makeTestConfigFactoryInstantiator
static makeTestConfigFactoryInstantiator(ConfigFactory $oldFactory, array $configurations)
Definition: MediaWikiTestCase.php:326
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:88
$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:83
MediaWikiTestCase\setUpBeforeClass
static setUpBeforeClass()
Definition: MediaWikiTestCase.php:138
$wgDiff3
$wgDiff3
Path to the GNU diff3 utility.
Definition: DefaultSettings.php:6610
MediaWikiTestCase\assertFileContains
assertFileContains( $fileName, $actualData, $createIfMissing=true, $msg='')
Check whether file contains given data.
Definition: MediaWikiTestCase.php:2027
MediaWikiTestCase\dropMockTables
dropMockTables(IMaintainableDatabase $db, array $tables)
Drops the given mock tables.
Definition: MediaWikiTestCase.php:1480
CACHE_NONE
const CACHE_NONE
Definition: Defines.php:112
MediaWikiTestCase\setupAllTestDBs
setupAllTestDBs()
Set up all test DBs.
Definition: MediaWikiTestCase.php:1205
MediaWikiTestCase\isWikitextNS
isWikitextNS( $ns)
Returns true if the given namespace defaults to Wikitext according to $wgNamespaceContentModels.
Definition: MediaWikiTestCase.php:1895
Maintenance\setLBFactoryTriggers
static setLBFactoryTriggers(LBFactory $LBFactory, Config $config)
Definition: Maintenance.php:623
MediaWikiTestCase\needsDB
needsDB()
Definition: MediaWikiTestCase.php:1006
CACHE_MEMCACHED
const CACHE_MEMCACHED
Definition: Defines.php:114
MediaWiki\MediaWikiServices\getBootstrapConfig
getBootstrapConfig()
Returns the Config object containing the bootstrap configuration.
Definition: MediaWikiServices.php:412
MediaWikiTestCase\installTestServices
static installTestServices(ConfigFactory $oldConfigFactory, LBFactory $oldLoadBalancerFactory, MediaWikiServices $newServices)
Definition: MediaWikiTestCase.php:295
Wikimedia\Rdbms\IDatabase\lastError
lastError()
Get a description of the last error.
PHPUnitMaintClass\$additionalOptions
static $additionalOptions
Definition: phpunit.php:18
MediaWikiTestCase\$useTemporaryTables
static $useTemporaryTables
Definition: MediaWikiTestCase.php:65
CACHE_ACCEL
const CACHE_ACCEL
Definition: Defines.php:115
unserialize
unserialize( $serialized)
Definition: ApiMessage.php:192
NS_FILE
const NS_FILE
Definition: Defines.php:80
MediaWikiTestCase\checkDbIsSupported
checkDbIsSupported()
Definition: MediaWikiTestCase.php:1640
serialize
serialize()
Definition: ApiMessage.php:184
MediaWikiTestCase\getCliArg
getCliArg( $offset)
Definition: MediaWikiTestCase.php:1651
MediaWikiTestCase\ensureMockDatabaseConnection
ensureMockDatabaseConnection(IDatabase $db)
Definition: MediaWikiTestCase.php:1339
MediaWikiTestCase\testMediaWikiTestCaseParentSetupCalled
testMediaWikiTestCaseParentSetupCalled()
Make sure MediaWikiTestCase extending classes have called their parent setUp method.
Definition: MediaWikiTestCase.php:613
MediaWikiTestCase\isNotUnittest
static isNotUnittest( $table)
Definition: MediaWikiTestCase.php:1596
$res
$res
Definition: database.txt:21
JobQueueGroup\destroySingletons
static destroySingletons()
Destroy the singleton instances.
Definition: JobQueueGroup.php:100
DeferredUpdates\clearPendingUpdates
static clearPendingUpdates()
Clear all pending updates without performing them.
Definition: DeferredUpdates.php:354
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:245
MediaWikiTestCase\$schemaOverrideDefaults
static $schemaOverrideDefaults
Definition: MediaWikiTestCase.php:1347
MediaWikiTestCase\prepareServices
static prepareServices(Config $bootstrapConfig)
Prepare service configuration for unit testing.
Definition: MediaWikiTestCase.php:200
$wgDefaultExternalStore
array $wgDefaultExternalStore
The place to put new revisions, false to put them in the local text table.
Definition: DefaultSettings.php:2171
$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. 'ImgAuthModifyHeaders':Executed just before a file is streamed to a user via img_auth.php, allowing headers to be modified beforehand. $title:LinkTarget object & $headers:HTTP headers(name=> value, names are case insensitive). Two headers get special handling:If-Modified-Since(value must be a valid HTTP date) and Range(must be of the form "bytes=(\d*-\d*)") will be honored when streaming the file. '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. 'LanguageGetMagic':DEPRECATED! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) '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 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) '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! 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:1993
MWNamespace\getContentNamespaces
static getContentNamespaces()
Get a list of all namespace indices which are considered to contain content.
Definition: MWNamespace.php:377
$wgDBprefix
$wgDBprefix
Table name prefix.
Definition: DefaultSettings.php:1871
MediaWikiTestCase\addDBDataOnce
addDBDataOnce()
Stub.
Definition: MediaWikiTestCase.php:1071
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:37
MediaWikiTestCase\teardownTestDB
static teardownTestDB()
Restores MediaWiki to using the table set (table prefix) it was using before setupTestDB() was called...
Definition: MediaWikiTestCase.php:1152
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:7
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:845
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:43
NS_MAIN
const NS_MAIN
Definition: Defines.php:74
MediaWiki\Services\ServiceContainer\redefineService
redefineService( $name, callable $instantiator)
Replace an already defined service.
Definition: ServiceContainer.php:238
MediaWikiTestCase\getExternalStoreDatabaseConnections
static getExternalStoreDatabaseConnections()
Gets master database connections for all of the ExternalStoreDB stores configured in $wgDefaultExtern...
Definition: MediaWikiTestCase.php:1295
Config
Interface for configuration instances.
Definition: Config.php:28
MediaWikiTestCase\setupDatabaseWithTestPrefix
static setupDatabaseWithTestPrefix(IMaintainableDatabase $db, $prefix)
Setups a database with the given prefix.
Definition: MediaWikiTestCase.php:1185
MediaWikiTestCase\undoSchemaOverrides
undoSchemaOverrides(IMaintainableDatabase $db, $oldOverrides)
Undoes the dpecified schema overrides.
Definition: MediaWikiTestCase.php:1379
Wikimedia\Rdbms\Database\delete
delete( $table, $conds, $fname=__METHOD__)
DELETE query wrapper.
Definition: Database.php:2830
MWException
MediaWiki exception.
Definition: MWException.php:26
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:115
NS_PROJECT
const NS_PROJECT
Definition: Defines.php:78
MediaWiki\MediaWikiServices\resetServiceForTesting
resetServiceForTesting( $name, $destroy=true)
Resets the given service for testing purposes.
Definition: MediaWikiServices.php:337
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1123
MediaWikiTestCase\$phpErrorLevel
int $phpErrorLevel
Original value of PHP's error_reporting setting.
Definition: MediaWikiTestCase.php:75
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:1518
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2812
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:678
MediaWikiTestCase\getNewTempFile
getNewTempFile()
Obtains a new temporary file name.
Definition: MediaWikiTestCase.php:462
MediaWikiTestCase\addCoreDBData
addCoreDBData()
Definition: MediaWikiTestCase.php:1086
MediaWikiTestCase\run
run(PHPUnit_Framework_TestResult $result=null)
Definition: MediaWikiTestCase.php:384
MediaWikiTestCase
Definition: MediaWikiTestCase.php:17
MediaWikiTestCase\$oldTablePrefix
static $oldTablePrefix
Definition: MediaWikiTestCase.php:68
MediaWikiTestCase\hideDeprecated
hideDeprecated( $function)
Don't throw a warning if $function is deprecated and called later.
Definition: MediaWikiTestCase.php:1675
MediaWikiTestCase\isUsingExternalStoreDB
static isUsingExternalStoreDB()
Check whether ExternalStoreDB is being used.
Definition: MediaWikiTestCase.php:1319
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:1701
MediaWikiTestCase\listTables
static listTables(IMaintainableDatabase $db)
Definition: MediaWikiTestCase.php:1607
MediaWikiTestCase\$users
static TestUser[] $users
Definition: MediaWikiTestCase.php:49
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:964
MediaWikiTestCase\doLightweightServiceReset
doLightweightServiceReset()
Resets some well known services that typically have state that may interfere with unit tests.
Definition: MediaWikiTestCase.php:362
GlobalVarConfig
Accesses configuration settings from $GLOBALS.
Definition: GlobalVarConfig.php:28
MediaWikiTestCase\getDefaultWikitextNS
getDefaultWikitextNS()
Returns the ID of a namespace that defaults to Wikitext.
Definition: MediaWikiTestCase.php:1912
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:95
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
NS_CATEGORY
const NS_CATEGORY
Definition: Defines.php:88
MediaWikiTestCase\setUserLang
setUserLang( $lang)
Definition: MediaWikiTestCase.php:883
DB_MASTER
const DB_MASTER
Definition: defines.php:26
WikitextContent
Content object for wiki text pages.
Definition: WikitextContent.php:33
MediaWikiTestCase\__construct
__construct( $name=null, array $data=[], $dataName='')
Definition: MediaWikiTestCase.php:123
MediaWiki\Session\SessionManager\resetCache
static resetCache()
Reset the internal caching for unit testing.
Definition: SessionManager.php:956
$services
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title 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:2273
MediaWiki\Auth\AuthManager\resetCache
static resetCache()
Reset the internal caching for unit testing.
Definition: AuthManager.php:2443
MediaWikiTestCase\arrayWrap
arrayWrap(array $elements)
Utility method taking an array of elements and wrapping each element in its own array.
Definition: MediaWikiTestCase.php:1750
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:1468
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:129
$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:2001
MediaWikiTestCase\setContentLang
setContentLang( $lang)
Definition: MediaWikiTestCase.php:892
MediaWikiTestCase\setupExternalStoreTestDBs
static setupExternalStoreTestDBs( $testPrefix)
Clones the External Store database(s) for testing.
Definition: MediaWikiTestCase.php:1274
MediaWikiTestCase\markTestSkippedIfNoDiff3
markTestSkippedIfNoDiff3()
Check, if $wgDiff3 is set and ready to merge Will mark the calling test as skipped,...
Definition: MediaWikiTestCase.php:1967
MediaWikiTestCase\resetGlobalServices
static resetGlobalServices(Config $bootstrapConfig=null)
Reset global services, and install testing environment.
Definition: MediaWikiTestCase.php:223
MediaWikiTestCase\$serviceLocator
static MediaWikiServices null $serviceLocator
The service locator created by prepareServices().
Definition: MediaWikiTestCase.php:29
RequestContext\resetMain
static resetMain()
Resets singleton returned by getMain().
Definition: RequestContext.php:460
Wikimedia\Rdbms\Database\tableExists
tableExists( $table, $fname=__METHOD__)
Query whether a given table exists.
Definition: Database.php:1884
MediaWikiTestCase\assertHTMLEquals
assertHTMLEquals( $expected, $actual, $msg='')
Put each HTML element on its own line and then equals() the results.
Definition: MediaWikiTestCase.php:1802
MediaWikiTestCase\setupTestDB
static setupTestDB(Database $db, $prefix)
Creates an empty skeleton of the wiki database by cloning its structure to equivalent tables using th...
Definition: MediaWikiTestCase.php:1241
MediaWikiTestCase\addTmpFiles
addTmpFiles( $files)
Definition: MediaWikiTestCase.php:530
$value
$value
Definition: styleTest.css.php:45
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:1859
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\getMutableTestUser
static getMutableTestUser( $groups=[])
Convenience method for getting a mutable test user.
Definition: MediaWikiTestCase.php:165
MediaWikiTestCase\dbPrefix
dbPrefix()
Definition: MediaWikiTestCase.php:998
MediaWikiTestCase\$mwGlobalsToUnset
array $mwGlobalsToUnset
Holds list of MediaWiki configuration settings to be unset in tearDown().
Definition: MediaWikiTestCase.php:98
MediaWikiTestCase\resetDB
resetDB( $db, $tablesUsed)
Empty all tables so they can be repopulated for tests.
Definition: MediaWikiTestCase.php:1540
MediaWikiTestCase\getTestSysop
static getTestSysop()
Convenience method for getting an immutable admin test user.
Definition: MediaWikiTestCase.php:177
Wikimedia\Rdbms\Database\tableName
tableName( $name, $format='quoted')
Format a table name ready for use in constructing an SQL query.
Definition: Database.php:2212
$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. '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:1255
MediaWikiTestCase\setGroupPermissions
setGroupPermissions( $newPerms, $newKey=null, $newValue=null)
Alters $wgGroupPermissions for the duration of the test.
Definition: MediaWikiTestCase.php:920
Wikimedia\Rdbms\Database\query
query( $sql, $fname=__METHOD__, $tempIgnore=false)
Run an SQL query and return the result.
Definition: Database.php:1094
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:434
MediaWikiTestCase\addDBData
addDBData()
Stub.
Definition: MediaWikiTestCase.php:1083
MediaWikiTestCase\setUp
setUp()
Definition: MediaWikiTestCase.php:492
$wgJobClasses
$wgJobClasses
Maps jobs to their handlers; extensions can add to this to provide custom jobs.
Definition: DefaultSettings.php:7477
MediaWikiTestCase\objectAssociativeSort
objectAssociativeSort(array &$array)
Does an associative sort that works for objects.
Definition: MediaWikiTestCase.php:1816
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:162
MediaWikiTestCase\DB_PREFIX
const DB_PREFIX
Table name prefixes.
Definition: MediaWikiTestCase.php:109
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
MonologSpi
MediaWiki Logger MonologSpi
Definition: logger.txt:58
$namespaces
namespace and then decline to actually register it & $namespaces
Definition: hooks.txt:934
wfTempDir
wfTempDir()
Tries to get the system directory for temporary files.
Definition: GlobalFunctions.php:2057
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:116
CloneDatabase
Definition: CloneDatabase.php:26
JobQueueGroup\singleton
static singleton( $domain=false)
Definition: JobQueueGroup.php:72
MediaWikiTestCase\canShallowCopy
static canShallowCopy( $value)
Check if we can back up a value by performing a shallow copy.
Definition: MediaWikiTestCase.php:698
MediaWikiTestCase\ORA_DB_PREFIX
const ORA_DB_PREFIX
Definition: MediaWikiTestCase.php:110
ObjectCache\getMainWANInstance
static getMainWANInstance()
Get the main WAN cache object.
Definition: ObjectCache.php:380
MediaWikiTestCase\setLogger
setLogger( $channel, LoggerInterface $logger)
Sets the logger for a specified channel, for the duration of the test.
Definition: MediaWikiTestCase.php:942
$tables
this hook is for auditing only RecentChangesLinked and Watchlist 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:1015
$wgGroupPermissions
$wgGroupPermissions
Permission keys given to users in each group.
Definition: DefaultSettings.php:5123
CloneDatabase\changePrefix
static changePrefix( $prefix)
Change the table prefix on all open DB connections/.
Definition: CloneDatabase.php:133
MediaWikiTestCase\insertPage
insertPage( $pageName, $text='Sample page for unit test.', $namespace=null)
Insert a new page.
Definition: MediaWikiTestCase.php:1033
User\resetIdByNameCache
static resetIdByNameCache()
Reset the cache used in idFromName().
Definition: User.php:923
wfRecursiveRemoveDir
wfRecursiveRemoveDir( $dir)
Remove a directory and all its content.
Definition: GlobalFunctions.php:2119
LinkCache\singleton
static singleton()
Get an instance of this class.
Definition: LinkCache.php:67
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:250
Wikimedia\Rdbms\LBFactory
An interface for generating database load balancers.
Definition: LBFactory.php:39
MediaWikiTestCase\$tablesUsed
array $tablesUsed
Definition: MediaWikiTestCase.php:63
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:22
MediaWikiTestCase\listOriginalTables
listOriginalTables(IMaintainableDatabase $db)
Lists all tables in the live database schema.
Definition: MediaWikiTestCase.php:1501
Wikimedia\Rdbms\IDatabase\getType
getType()
Get the type of the DBMS, as it appears in $wgDBtype.
MediaWikiTestCase\oncePerClass
oncePerClass()
Definition: MediaWikiTestCase.php:431
ConfigFactory
Factory class to create Config objects.
Definition: ConfigFactory.php:31
MediaWikiTestCase\containsClosure
containsClosure( $var, $maxDepth=15)
Definition: MediaWikiTestCase.php:782
NS_USER
const NS_USER
Definition: Defines.php:76
MediaWikiTestCase\checkPHPExtension
checkPHPExtension( $extName)
Check if $extName is a loaded PHP extension, will skip the test whenever it is not loaded.
Definition: MediaWikiTestCase.php:1989
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
Language\factory
static factory( $code)
Get a cached or new language object for a given language code.
Definition: Language.php:183
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:1649
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:82
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:56
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:165
MediaWikiTestCase\$mwGlobals
array $mwGlobals
Holds original values of MediaWiki configuration settings to be restored in tearDown().
Definition: MediaWikiTestCase.php:91
MediaWikiTestCase\$loggers
LoggerInterface[] $loggers
Holds original loggers which have been replaced by setLogger()
Definition: MediaWikiTestCase.php:104
MediaWikiTestCase\setTemporaryHook
setTemporaryHook( $hookName, $handler)
Create a temporary hook handler which will be reset by tearDown.
Definition: MediaWikiTestCase.php:2014
$wgRequest
if(! $wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:737
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:25
MediaWikiTestCase\usesTemporaryTables
usesTemporaryTables()
Definition: MediaWikiTestCase.php:449
MediaWikiTestCase\assertType
assertType( $type, $actual, $message='')
Asserts the type of the provided value.
Definition: MediaWikiTestCase.php:1878
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:4108
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:1664
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:53
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:203
MediaWikiTestCase\setService
setService( $name, $object)
Sets a service, maintaining a stashed version of the previous service to be restored in tearDown.
Definition: MediaWikiTestCase.php:628
Wikimedia\Rdbms\Database\listViews
listViews( $prefix=null, $fname=__METHOD__)
Lists all the VIEWs in the database.
Definition: Database.php:3902
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:1547
Wikimedia\Rdbms\IMaintainableDatabase
Advanced database interface for IDatabase handles that include maintenance methods.
Definition: IMaintainableDatabase.php:38
MediaWikiTestCase\__destruct
__destruct()
Definition: MediaWikiTestCase.php:130
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
Language
Internationalisation code.
Definition: Language.php:35
MediaWikiTestCase\unprefixTable
static unprefixTable(&$tableName, $ind, $prefix)
Definition: MediaWikiTestCase.php:1592
$GLOBALS
$GLOBALS['IP']
Definition: ComposerHookHandler.php:6
MediaWikiTestCase\$db
Database $db
Primary database.
Definition: MediaWikiTestCase.php:57
$buffer
$buffer
Definition: mwdoc-filter.php:49
CACHE_DB
const CACHE_DB
Definition: Defines.php:113
MediaWikiTestCase\tearDown
tearDown()
Definition: MediaWikiTestCase.php:534
MediaWikiTestCase\setUpSchema
setUpSchema(IMaintainableDatabase $db)
Applies the schema overrides returned by getSchemaOverrides(), after undoing any previously applied s...
Definition: MediaWikiTestCase.php:1407
MediaWikiTestCase\$dbSetup
static $dbSetup
Definition: MediaWikiTestCase.php:67
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2171
$wgSQLMode
$wgSQLMode
SQL Mode - default is turning off all modes, including strict, if set.
Definition: DefaultSettings.php:1884
TestUserRegistry\clear
static clear()
Clear the registry.
Definition: TestUserRegistry.php:107
$type
$type
Definition: testCompression.php:48
MediaWikiTestCase\getNewTempDirectory
getNewTempDirectory()
obtains a new temporary directory
Definition: MediaWikiTestCase.php:479