Пример #1
0
 /**
  * @return LockManagerGroup
  */
 public static function singleton()
 {
     if (self::$instance == null) {
         self::$instance = new self();
         self::$instance->initFromGlobals();
     }
     return self::$instance;
 }
Пример #2
0
 protected function setUp()
 {
     global $wgFileBackends;
     parent::setUp();
     $uniqueId = time() . '-' . mt_rand();
     $tmpPrefix = wfTempDir() . '/filebackend-unittest-' . $uniqueId;
     if ($this->getCliArg('use-filebackend=')) {
         if (self::$backendToUse) {
             $this->singleBackend = self::$backendToUse;
         } else {
             $name = $this->getCliArg('use-filebackend=');
             $useConfig = array();
             foreach ($wgFileBackends as $conf) {
                 if ($conf['name'] == $name) {
                     $useConfig = $conf;
                     break;
                 }
             }
             $useConfig['name'] = 'localtesting';
             // swap name
             $useConfig['shardViaHashLevels'] = array('unittest-cont1' => array('levels' => 1, 'base' => 16, 'repeat' => 1));
             if (isset($useConfig['fileJournal'])) {
                 $useConfig['fileJournal'] = FileJournal::factory($useConfig['fileJournal'], $name);
             }
             $useConfig['lockManager'] = LockManagerGroup::singleton()->get($useConfig['lockManager']);
             $class = $useConfig['class'];
             self::$backendToUse = new $class($useConfig);
             $this->singleBackend = self::$backendToUse;
         }
     } else {
         $this->singleBackend = new FSFileBackend(array('name' => 'localtesting', 'lockManager' => LockManagerGroup::singleton()->get('fsLockManager'), 'wikiId' => wfWikiID(), 'containerPaths' => array('unittest-cont1' => "{$tmpPrefix}-localtesting-cont1", 'unittest-cont2' => "{$tmpPrefix}-localtesting-cont2")));
     }
     $this->multiBackend = new FileBackendMultiWrite(array('name' => 'localtesting', 'lockManager' => LockManagerGroup::singleton()->get('fsLockManager'), 'parallelize' => 'implicit', 'wikiId' => wfWikiId() . $uniqueId, 'backends' => array(array('name' => 'localmultitesting1', 'class' => 'FSFileBackend', 'containerPaths' => array('unittest-cont1' => "{$tmpPrefix}-localtestingmulti1-cont1", 'unittest-cont2' => "{$tmpPrefix}-localtestingmulti1-cont2"), 'isMultiMaster' => false), array('name' => 'localmultitesting2', 'class' => 'FSFileBackend', 'containerPaths' => array('unittest-cont1' => "{$tmpPrefix}-localtestingmulti2-cont1", 'unittest-cont2' => "{$tmpPrefix}-localtestingmulti2-cont2"), 'isMultiMaster' => true))));
     $this->filesToPrune = array();
 }
Пример #3
0
 protected function setUp()
 {
     global $wgFileBackends;
     parent::setUp();
     # Forge a FSRepo object to not have to rely on local wiki settings
     $tmpPrefix = wfTempDir() . '/storebatch-test-' . time() . '-' . mt_rand();
     if ($this->getCliArg('use-filebackend=')) {
         $name = $this->getCliArg('use-filebackend=');
         $useConfig = array();
         foreach ($wgFileBackends as $conf) {
             if ($conf['name'] == $name) {
                 $useConfig = $conf;
             }
         }
         $useConfig['lockManager'] = LockManagerGroup::singleton()->get($useConfig['lockManager']);
         unset($useConfig['fileJournal']);
         $useConfig['name'] = 'local-testing';
         // swap name
         $class = $useConfig['class'];
         $backend = new $class($useConfig);
     } else {
         $backend = new FSFileBackend(array('name' => 'local-testing', 'wikiId' => wfWikiID(), 'containerPaths' => array('unittests-public' => "{$tmpPrefix}-public", 'unittests-thumb' => "{$tmpPrefix}-thumb", 'unittests-temp' => "{$tmpPrefix}-temp", 'unittests-deleted' => "{$tmpPrefix}-deleted")));
     }
     $this->repo = new FileRepo(array('name' => 'unittests', 'backend' => $backend));
     $this->date = gmdate("YmdHis");
     $this->createdFiles = array();
 }
Пример #4
0
 protected function setUp()
 {
     global $wgFileBackends;
     parent::setUp();
     $tmpDir = $this->getNewTempDirectory();
     if ($this->getCliArg('use-filebackend')) {
         if (self::$backendToUse) {
             $this->singleBackend = self::$backendToUse;
         } else {
             $name = $this->getCliArg('use-filebackend');
             $useConfig = [];
             foreach ($wgFileBackends as $conf) {
                 if ($conf['name'] == $name) {
                     $useConfig = $conf;
                     break;
                 }
             }
             $useConfig['name'] = 'localtesting';
             // swap name
             $useConfig['shardViaHashLevels'] = ['unittest-cont1' => ['levels' => 1, 'base' => 16, 'repeat' => 1]];
             if (isset($useConfig['fileJournal'])) {
                 $useConfig['fileJournal'] = FileJournal::factory($useConfig['fileJournal'], $name);
             }
             $useConfig['lockManager'] = LockManagerGroup::singleton()->get($useConfig['lockManager']);
             $class = $useConfig['class'];
             self::$backendToUse = new $class($useConfig);
             $this->singleBackend = self::$backendToUse;
         }
     } else {
         $this->singleBackend = new FSFileBackend(['name' => 'localtesting', 'lockManager' => LockManagerGroup::singleton()->get('fsLockManager'), 'wikiId' => wfWikiID(), 'containerPaths' => ['unittest-cont1' => "{$tmpDir}/localtesting-cont1", 'unittest-cont2' => "{$tmpDir}/localtesting-cont2"]]);
     }
     $this->multiBackend = new FileBackendMultiWrite(['name' => 'localtesting', 'lockManager' => LockManagerGroup::singleton()->get('fsLockManager'), 'parallelize' => 'implicit', 'wikiId' => wfWikiID() . wfRandomString(), 'backends' => [['name' => 'localmultitesting1', 'class' => 'FSFileBackend', 'containerPaths' => ['unittest-cont1' => "{$tmpDir}/localtestingmulti1-cont1", 'unittest-cont2' => "{$tmpDir}/localtestingmulti1-cont2"], 'isMultiMaster' => false], ['name' => 'localmultitesting2', 'class' => 'FSFileBackend', 'containerPaths' => ['unittest-cont1' => "{$tmpDir}/localtestingmulti2-cont1", 'unittest-cont2' => "{$tmpDir}/localtestingmulti2-cont2"], 'isMultiMaster' => true]]]);
 }
Пример #5
0
 /**
  * Create a new backend instance from configuration.
  * This should only be called from within FileBackendGroup.
  * 
  * $config includes:
  *     'name'        : The unique name of this backend.
  *                     This should consist of alphanumberic, '-', and '_' characters.
  *                     This name should not be changed after use.
  *     'wikiId'      : Prefix to container names that is unique to this wiki.
  *                     This should consist of alphanumberic, '-', and '_' characters.
  *     'lockManager' : Registered name of a file lock manager to use.
  *     'readOnly'    : Write operations are disallowed if this is a non-empty string.
  *                     It should be an explanation for the backend being read-only.
  * 
  * @param $config Array
  */
 public function __construct(array $config)
 {
     $this->name = $config['name'];
     if (!preg_match('!^[a-zA-Z0-9-_]{1,255}$!', $this->name)) {
         throw new MWException("Backend name `{$this->name}` is invalid.");
     }
     $this->wikiId = isset($config['wikiId']) ? $config['wikiId'] : wfWikiID();
     // e.g. "my_wiki-en_"
     $this->lockManager = $config['lockManager'] instanceof LockManager ? $config['lockManager'] : LockManagerGroup::singleton()->get($config['lockManager']);
     $this->readOnly = isset($config['readOnly']) ? (string) $config['readOnly'] : '';
 }
Пример #6
0
 /**
  * @param array $info
  * @throws MWException
  */
 function __construct(array $info)
 {
     if (!isset($info['backend'])) {
         // B/C settings...
         $directory = $info['directory'];
         $deletedDir = isset($info['deletedDir']) ? $info['deletedDir'] : false;
         $thumbDir = isset($info['thumbDir']) ? $info['thumbDir'] : "{$directory}/thumb";
         $transcodedDir = isset($info['transcodedDir']) ? $info['transcodedDir'] : "{$directory}/transcoded";
         $fileMode = isset($info['fileMode']) ? $info['fileMode'] : 0644;
         $repoName = $info['name'];
         // Get the FS backend configuration
         $backend = new FSFileBackend(['name' => $info['name'] . '-backend', 'wikiId' => wfWikiID(), 'lockManager' => LockManagerGroup::singleton(wfWikiID())->get('fsLockManager'), 'containerPaths' => ["{$repoName}-public" => "{$directory}", "{$repoName}-temp" => "{$directory}/temp", "{$repoName}-thumb" => $thumbDir, "{$repoName}-transcoded" => $transcodedDir, "{$repoName}-deleted" => $deletedDir], 'fileMode' => $fileMode, 'tmpDirectory' => wfTempDir()]);
         // Update repo config to use this backend
         $info['backend'] = $backend;
     }
     parent::__construct($info);
     if (!$this->backend instanceof FSFileBackend) {
         throw new MWException("FSRepo only supports FSFileBackend.");
     }
 }
Пример #7
0
 /**
  * Get the backend object with a given name
  *
  * @param string $name
  * @return FileBackend
  * @throws FileBackendException
  */
 public function get($name)
 {
     if (!isset($this->backends[$name])) {
         throw new FileBackendException("No backend defined with the name `{$name}`.");
     }
     // Lazy-load the actual backend instance
     if (!isset($this->backends[$name]['instance'])) {
         $class = $this->backends[$name]['class'];
         $config = $this->backends[$name]['config'];
         $config['wikiId'] = isset($config['wikiId']) ? $config['wikiId'] : wfWikiID();
         // e.g. "my_wiki-en_"
         $config['lockManager'] = LockManagerGroup::singleton($config['wikiId'])->get($config['lockManager']);
         $config['fileJournal'] = isset($config['fileJournal']) ? FileJournal::factory($config['fileJournal'], $name) : FileJournal::factory(array('class' => 'NullFileJournal'), $name);
         $config['wanCache'] = ObjectCache::getMainWANInstance();
         $this->backends[$name]['instance'] = new $class($config);
     }
     return $this->backends[$name]['instance'];
 }
Пример #8
0
	/**
	 * Create a new backend instance from configuration.
	 * This should only be called from within FileBackendGroup.
	 *
	 * $config includes:
	 *   - name        : The unique name of this backend.
	 *                   This should consist of alphanumberic, '-', and '_' characters.
	 *                   This name should not be changed after use (e.g. with journaling).
	 *                   Note that the name is *not* used in actual container names.
	 *   - wikiId      : Prefix to container names that is unique to this backend.
	 *                   If not provided, this defaults to the current wiki ID.
	 *                   It should only consist of alphanumberic, '-', and '_' characters.
	 *                   This ID is what avoids collisions if multiple logical backends
	 *                   use the same storage system, so this should be set carefully.
	 *   - lockManager : Registered name of a file lock manager to use.
	 *   - fileJournal : File journal configuration; see FileJournal::factory().
	 *                   Journals simply log changes to files stored in the backend.
	 *   - readOnly    : Write operations are disallowed if this is a non-empty string.
	 *                   It should be an explanation for the backend being read-only.
	 *   - parallelize : When to do file operations in parallel (when possible).
	 *                   Allowed values are "implicit", "explicit" and "off".
	 *   - concurrency : How many file operations can be done in parallel.
	 *
	 * @param array $config
	 * @throws MWException
	 */
	public function __construct( array $config ) {
		$this->name = $config['name'];
		if ( !preg_match( '!^[a-zA-Z0-9-_]{1,255}$!', $this->name ) ) {
			throw new MWException( "Backend name `{$this->name}` is invalid." );
		}
		$this->wikiId = isset( $config['wikiId'] )
			? $config['wikiId']
			: wfWikiID(); // e.g. "my_wiki-en_"
		$this->lockManager = ( $config['lockManager'] instanceof LockManager )
			? $config['lockManager']
			: LockManagerGroup::singleton( $this->wikiId )->get( $config['lockManager'] );
		$this->fileJournal = isset( $config['fileJournal'] )
			? ( ( $config['fileJournal'] instanceof FileJournal )
				? $config['fileJournal']
				: FileJournal::factory( $config['fileJournal'], $this->name ) )
			: FileJournal::factory( array( 'class' => 'NullFileJournal' ), $this->name );
		$this->readOnly = isset( $config['readOnly'] )
			? (string)$config['readOnly']
			: '';
		$this->parallelize = isset( $config['parallelize'] )
			? (string)$config['parallelize']
			: 'off';
		$this->concurrency = isset( $config['concurrency'] )
			? (int)$config['concurrency']
			: 50;
	}
Пример #9
0
 /**
  * Destroy the singleton instances
  */
 public static function destroySingletons()
 {
     self::$instances = array();
 }
Пример #10
0
 protected function prepareEnvironment()
 {
     global $wgMemc;
     // Don't share DB, storage, or memcached connections
     MediaWikiServices::resetChildProcessServices();
     FileBackendGroup::destroySingleton();
     LockManagerGroup::destroySingletons();
     JobQueueGroup::destroySingletons();
     ObjectCache::clear();
     RedisConnectionPool::destroySingletons();
     $wgMemc = null;
 }
 protected function setUp()
 {
     parent::setUp();
     $this->backend = TestingAccessWrapper::newFromObject(new SwiftFileBackend(array('name' => 'local-swift-testing', 'class' => 'SwiftFileBackend', 'wikiId' => 'unit-testing', 'lockManager' => LockManagerGroup::singleton()->get('fsLockManager'), 'swiftAuthUrl' => 'http://127.0.0.1:8080/auth', 'swiftUser' => 'test:tester', 'swiftKey' => 'testing', 'swiftTempUrlKey' => 'b3968d0207b54ece87cccc06515a89d4')));
 }
Пример #12
0
 /**
  * Create a new backend instance from configuration.
  * This should only be called from within FileBackendGroup.
  *
  * @param array $config Parameters include:
  *   - name        : The unique name of this backend.
  *                   This should consist of alphanumberic, '-', and '_' characters.
  *                   This name should not be changed after use (e.g. with journaling).
  *                   Note that the name is *not* used in actual container names.
  *   - wikiId      : Prefix to container names that is unique to this backend.
  *                   It should only consist of alphanumberic, '-', and '_' characters.
  *                   This ID is what avoids collisions if multiple logical backends
  *                   use the same storage system, so this should be set carefully.
  *   - lockManager : LockManager object to use for any file locking.
  *                   If not provided, then no file locking will be enforced.
  *   - fileJournal : FileJournal object to use for logging changes to files.
  *                   If not provided, then change journaling will be disabled.
  *   - readOnly    : Write operations are disallowed if this is a non-empty string.
  *                   It should be an explanation for the backend being read-only.
  *   - parallelize : When to do file operations in parallel (when possible).
  *                   Allowed values are "implicit", "explicit" and "off".
  *   - concurrency : How many file operations can be done in parallel.
  * @throws FileBackendException
  */
 public function __construct(array $config)
 {
     $this->name = $config['name'];
     if (!preg_match('!^[a-zA-Z0-9-_]{1,255}$!', $this->name)) {
         throw new FileBackendException("Backend name `{$this->name}` is invalid.");
     }
     if (!isset($config['wikiId'])) {
         $config['wikiId'] = wfWikiID();
         wfDeprecated(__METHOD__ . ' called without "wikiID".', '1.23');
     }
     if (isset($config['lockManager']) && !is_object($config['lockManager'])) {
         $config['lockManager'] = LockManagerGroup::singleton($config['wikiId'])->get($config['lockManager']);
         wfDeprecated(__METHOD__ . ' called with non-object "lockManager".', '1.23');
     }
     $this->wikiId = $config['wikiId'];
     // e.g. "my_wiki-en_"
     $this->lockManager = isset($config['lockManager']) ? $config['lockManager'] : new NullLockManager(array());
     $this->fileJournal = isset($config['fileJournal']) ? $config['fileJournal'] : FileJournal::factory(array('class' => 'NullFileJournal'), $this->name);
     $this->readOnly = isset($config['readOnly']) ? (string) $config['readOnly'] : '';
     $this->parallelize = isset($config['parallelize']) ? (string) $config['parallelize'] : 'off';
     $this->concurrency = isset($config['concurrency']) ? (int) $config['concurrency'] : 50;
 }
Пример #13
0
 protected function prepareEnvironment()
 {
     global $wgMemc;
     // Don't share DB, storage, or memcached connections
     wfGetLBFactory()->destroyInstance();
     FileBackendGroup::destroySingleton();
     LockManagerGroup::destroySingletons();
     ObjectCache::clear();
     $wgMemc = null;
 }
Пример #14
0
 /**
  * Do any setup which can be done once for all tests, independent of test
  * options, except for database setup.
  *
  * Public setup functions in this class return a ScopedCallback object. When
  * this object is destroyed by going out of scope, teardown of the
  * corresponding test setup is performed.
  *
  * Teardown objects may be chained by passing a ScopedCallback from a
  * previous setup stage as the $nextTeardown parameter. This enforces the
  * convention that teardown actions are taken in reverse order to the
  * corresponding setup actions. When $nextTeardown is specified, a
  * ScopedCallback will be returned which first tears down the current
  * setup stage, and then tears down the previous setup stage which was
  * specified by $nextTeardown.
  *
  * @param ScopedCallback|null $nextTeardown
  * @return ScopedCallback
  */
 public function staticSetup($nextTeardown = null)
 {
     // A note on coding style:
     // The general idea here is to keep setup code together with
     // corresponding teardown code, in a fine-grained manner. We have two
     // arrays: $setup and $teardown. The code snippets in the $setup array
     // are executed at the end of the method, before it returns, and the
     // code snippets in the $teardown array are executed in reverse order
     // when the Wikimedia\ScopedCallback object is consumed.
     // Because it is a common operation to save, set and restore global
     // variables, we have an additional convention: when the array key of
     // $setup is a string, the string is taken to be the name of the global
     // variable, and the element value is taken to be the desired new value.
     // It's acceptable to just do the setup immediately, instead of adding
     // a closure to $setup, except when the setup action depends on global
     // variable initialisation being done first. In this case, you have to
     // append a closure to $setup after the global variable is appended.
     // When you add to setup functions in this class, please keep associated
     // setup and teardown actions together in the source code, and please
     // add comments explaining why the setup action is necessary.
     $setup = [];
     $teardown = [];
     $teardown[] = $this->markSetupDone('staticSetup');
     // Some settings which influence HTML output
     $setup['wgSitename'] = 'MediaWiki';
     $setup['wgServer'] = 'http://example.org';
     $setup['wgServerName'] = 'example.org';
     $setup['wgScriptPath'] = '';
     $setup['wgScript'] = '/index.php';
     $setup['wgResourceBasePath'] = '';
     $setup['wgStylePath'] = '/skins';
     $setup['wgExtensionAssetsPath'] = '/extensions';
     $setup['wgArticlePath'] = '/wiki/$1';
     $setup['wgActionPaths'] = [];
     $setup['wgVariantArticlePath'] = false;
     $setup['wgUploadNavigationUrl'] = false;
     $setup['wgCapitalLinks'] = true;
     $setup['wgNoFollowLinks'] = true;
     $setup['wgNoFollowDomainExceptions'] = ['no-nofollow.org'];
     $setup['wgExternalLinkTarget'] = false;
     $setup['wgExperimentalHtmlIds'] = false;
     $setup['wgLocaltimezone'] = 'UTC';
     $setup['wgHtml5'] = true;
     $setup['wgDisableLangConversion'] = false;
     $setup['wgDisableTitleConversion'] = false;
     // "extra language links"
     // see https://gerrit.wikimedia.org/r/111390
     $setup['wgExtraInterlanguageLinkPrefixes'] = ['mul'];
     // All FileRepo changes should be done here by injecting services,
     // there should be no need to change global variables.
     RepoGroup::setSingleton($this->createRepoGroup());
     $teardown[] = function () {
         RepoGroup::destroySingleton();
     };
     // Set up null lock managers
     $setup['wgLockManagers'] = [['name' => 'fsLockManager', 'class' => 'NullLockManager'], ['name' => 'nullLockManager', 'class' => 'NullLockManager']];
     $reset = function () {
         LockManagerGroup::destroySingletons();
     };
     $setup[] = $reset;
     $teardown[] = $reset;
     // This allows article insertion into the prefixed DB
     $setup['wgDefaultExternalStore'] = false;
     // This might slightly reduce memory usage
     $setup['wgAdaptiveMessageCache'] = true;
     // This is essential and overrides disabling of database messages in TestSetup
     $setup['wgUseDatabaseMessages'] = true;
     $reset = function () {
         MessageCache::destroyInstance();
     };
     $setup[] = $reset;
     $teardown[] = $reset;
     // It's not necessary to actually convert any files
     $setup['wgSVGConverter'] = 'null';
     $setup['wgSVGConverters'] = ['null' => 'echo "1">$output'];
     // Fake constant timestamp
     Hooks::register('ParserGetVariableValueTs', 'ParserTestRunner::getFakeTimestamp');
     $teardown[] = function () {
         Hooks::clear('ParserGetVariableValueTs');
     };
     $this->appendNamespaceSetup($setup, $teardown);
     // Set up interwikis and append teardown function
     $teardown[] = $this->setupInterwikis();
     // This affects title normalization in links. It invalidates
     // MediaWikiTitleCodec objects.
     $setup['wgLocalInterwikis'] = ['local', 'mi'];
     $reset = function () {
         $this->resetTitleServices();
     };
     $setup[] = $reset;
     $teardown[] = $reset;
     // Set up a mock MediaHandlerFactory
     MediaWikiServices::getInstance()->disableService('MediaHandlerFactory');
     MediaWikiServices::getInstance()->redefineService('MediaHandlerFactory', function () {
         return new MockMediaHandlerFactory();
     });
     $teardown[] = function () {
         MediaWikiServices::getInstance()->resetServiceForTesting('MediaHandlerFactory');
     };
     // SqlBagOStuff broke when using temporary tables on r40209 (bug 15892).
     // It seems to have been fixed since (r55079?), but regressed at some point before r85701.
     // This works around it for now...
     global $wgObjectCaches;
     $setup['wgObjectCaches'] = [CACHE_DB => $wgObjectCaches['hash']] + $wgObjectCaches;
     if (isset(ObjectCache::$instances[CACHE_DB])) {
         $savedCache = ObjectCache::$instances[CACHE_DB];
         ObjectCache::$instances[CACHE_DB] = new HashBagOStuff();
         $teardown[] = function () use($savedCache) {
             ObjectCache::$instances[CACHE_DB] = $savedCache;
         };
     }
     $teardown[] = $this->executeSetupSnippets($setup);
     // Schedule teardown snippets in reverse order
     return $this->createTeardownObject($teardown, $nextTeardown);
 }
Пример #15
0
 /**
  * Get the config array for a backend object with a given name
  *
  * @param string $name
  * @return array Parameters to FileBackend::__construct()
  * @throws InvalidArgumentException
  */
 public function config($name)
 {
     if (!isset($this->backends[$name])) {
         throw new InvalidArgumentException("No backend defined with the name `{$name}`.");
     }
     $class = $this->backends[$name]['class'];
     $config = $this->backends[$name]['config'];
     $config['class'] = $class;
     $config += ['wikiId' => wfWikiID(), 'mimeCallback' => [$this, 'guessMimeInternal'], 'obResetFunc' => 'wfResetOutputBuffers', 'streamMimeFunc' => ['StreamFile', 'contentTypeFromPath'], 'tmpDirectory' => wfTempDir(), 'statusWrapper' => ['Status', 'wrap'], 'wanCache' => MediaWikiServices::getInstance()->getMainWANObjectCache(), 'srvCache' => ObjectCache::getLocalServerInstance('hash'), 'logger' => LoggerFactory::getInstance('FileOperation'), 'profiler' => Profiler::instance()];
     $config['lockManager'] = LockManagerGroup::singleton($config['wikiId'])->get($config['lockManager']);
     $config['fileJournal'] = isset($config['fileJournal']) ? FileJournal::factory($config['fileJournal'], $name) : FileJournal::factory(['class' => 'NullFileJournal'], $name);
     return $config;
 }
Пример #16
0
 /**
  * Destroy the singleton instance, so that a new one will be created next
  * time singleton() is called.
  */
 public static function destroySingleton()
 {
     self::$instance = null;
 }