MediaWiki  REL1_19
MediaWikiTestCase.php
Go to the documentation of this file.
00001 <?php
00002 
00003 abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
00004         public $suite;
00005         public $regex = '';
00006         public $runDisabled = false;
00007 
00011         protected $db;
00012         protected $oldTablePrefix;
00013         protected $useTemporaryTables = true;
00014         protected $reuseDB = false;
00015         protected $tablesUsed = array(); // tables with data
00016 
00017         private static $dbSetup = false;
00018 
00022         const DB_PREFIX = 'unittest_';
00023         const ORA_DB_PREFIX = 'ut_';
00024 
00025         protected $supportedDBs = array(
00026                 'mysql',
00027                 'sqlite',
00028                 'postgres',
00029                 'oracle'
00030         );
00031 
00032         function  __construct( $name = null, array $data = array(), $dataName = '' ) {
00033                 parent::__construct( $name, $data, $dataName );
00034 
00035                 $this->backupGlobals = false;
00036                 $this->backupStaticAttributes = false;
00037         }
00038 
00039         function run( PHPUnit_Framework_TestResult $result = NULL ) {
00040                 /* Some functions require some kind of caching, and will end up using the db,
00041                  * which we can't allow, as that would open a new connection for mysql.
00042                  * Replace with a HashBag. They would not be going to persist anyway.
00043                  */
00044                 ObjectCache::$instances[CACHE_DB] = new HashBagOStuff;
00045 
00046                 if( $this->needsDB() ) {
00047                         global $wgDBprefix;
00048                         
00049                         $this->useTemporaryTables = !$this->getCliArg( 'use-normal-tables' );
00050                         $this->reuseDB = $this->getCliArg('reuse-db');
00051 
00052                         $this->db = wfGetDB( DB_MASTER );
00053 
00054                         $this->checkDbIsSupported();
00055 
00056                         $this->oldTablePrefix = $wgDBprefix;
00057 
00058                         if( !self::$dbSetup ) {
00059                                 $this->initDB();
00060                                 self::$dbSetup = true;
00061                         }
00062 
00063                         $this->addCoreDBData();
00064                         $this->addDBData();
00065 
00066                         parent::run( $result );
00067 
00068                         $this->resetDB();
00069                 } else {
00070                         parent::run( $result );
00071                 }
00072         }
00073 
00074         function dbPrefix() {
00075                 return $this->db->getType() == 'oracle' ? self::ORA_DB_PREFIX : self::DB_PREFIX;
00076         }
00077 
00078         function needsDB() {
00079                 $rc = new ReflectionClass( $this );
00080                 return strpos( $rc->getDocComment(), '@group Database' ) !== false;
00081         }
00082 
00087         function addDBData() {}
00088 
00089         private function addCoreDBData() {
00090                 # disabled for performance
00091                 #$this->tablesUsed[] = 'page';
00092                 #$this->tablesUsed[] = 'revision';
00093 
00094                 if ( $this->db->getType() == 'oracle' ) {
00095 
00096                         # Insert 0 user to prevent FK violations
00097                         # Anonymous user
00098                         $this->db->insert( 'user', array(
00099                                 'user_id'               => 0,
00100                                 'user_name'     => 'Anonymous' ), __METHOD__, array( 'IGNORE' ) );
00101 
00102                         # Insert 0 page to prevent FK violations
00103                         # Blank page
00104                         $this->db->insert( 'page', array(
00105                                 'page_id' => 0,
00106                                 'page_namespace' => 0,
00107                                 'page_title' => ' ',
00108                                 'page_restrictions' => NULL,
00109                                 'page_counter' => 0,
00110                                 'page_is_redirect' => 0,
00111                                 'page_is_new' => 0,
00112                                 'page_random' => 0,
00113                                 'page_touched' => $this->db->timestamp(),
00114                                 'page_latest' => 0,
00115                                 'page_len' => 0 ), __METHOD__, array( 'IGNORE' ) );
00116 
00117                 }
00118 
00119                 User::resetIdByNameCache();
00120 
00121                 //Make sysop user
00122                 $user = User::newFromName( 'UTSysop' );
00123 
00124                 if ( $user->idForName() == 0 ) {
00125                         $user->addToDatabase();
00126                         $user->setPassword( 'UTSysopPassword' );
00127 
00128                         $user->addGroup( 'sysop' );
00129                         $user->addGroup( 'bureaucrat' );
00130                         $user->saveSettings();
00131                 }
00132 
00133 
00134                 //Make 1 page with 1 revision
00135                 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
00136                 if ( !$page->getId() == 0 ) {
00137                         $page->doEdit( 'UTContent',
00138                                                         'UTPageSummary',
00139                                                         EDIT_NEW,
00140                                                         false,
00141                                                         User::newFromName( 'UTSysop' ) );
00142                 }
00143         }
00144 
00145         private function initDB() {
00146                 global $wgDBprefix;
00147                 if ( $wgDBprefix === $this->dbPrefix() ) {
00148                         throw new MWException( 'Cannot run unit tests, the database prefix is already "unittest_"' );
00149                 }
00150 
00151                 $tablesCloned = $this->listTables();
00152                 $dbClone = new CloneDatabase( $this->db, $tablesCloned, $this->dbPrefix() );
00153                 $dbClone->useTemporaryTables( $this->useTemporaryTables );
00154 
00155                 if ( ( $this->db->getType() == 'oracle' || !$this->useTemporaryTables ) && $this->reuseDB ) {
00156                         CloneDatabase::changePrefix( $this->dbPrefix() );
00157                         $this->resetDB();
00158                         return;
00159                 } else {
00160                         $dbClone->cloneTableStructure();
00161                 }
00162 
00163                 if ( $this->db->getType() == 'oracle' ) {
00164                         $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
00165                 }
00166         }
00167 
00171         private function resetDB() {
00172                 if( $this->db ) {
00173                         if ( $this->db->getType() == 'oracle' )  {
00174                                 if ( $this->useTemporaryTables ) {
00175                                         wfGetLB()->closeAll();
00176                                         $this->db = wfGetDB( DB_MASTER );
00177                                 } else {
00178                                         foreach( $this->tablesUsed as $tbl ) {
00179                                                 if( $tbl == 'interwiki') continue;
00180                                                 $this->db->query( 'TRUNCATE TABLE '.$this->db->tableName($tbl), __METHOD__ );
00181                                         }
00182                                 }
00183                         } else {
00184                                 foreach( $this->tablesUsed as $tbl ) {
00185                                         if( $tbl == 'interwiki' || $tbl == 'user' ) continue;
00186                                         $this->db->delete( $tbl, '*', __METHOD__ );
00187                                 }
00188                         }
00189                 }
00190         }
00191 
00192         function __call( $func, $args ) {
00193                 static $compatibility = array(
00194                         'assertInternalType' => 'assertType',
00195                         'assertNotInternalType' => 'assertNotType',
00196                         'assertInstanceOf' => 'assertType',
00197                         'assertEmpty' => 'assertEmpty2',
00198                 );
00199 
00200                 if ( method_exists( $this->suite, $func ) ) {
00201                         return call_user_func_array( array( $this->suite, $func ), $args);
00202                 } elseif ( isset( $compatibility[$func] ) ) {
00203                         return call_user_func_array( array( $this, $compatibility[$func] ), $args);
00204                 } else {
00205                         throw new MWException( "Called non-existant $func method on "
00206                                 . get_class( $this ) );
00207                 }
00208         }
00209 
00210         private function assertEmpty2( $value, $msg ) {
00211                 return $this->assertTrue( $value == '', $msg );
00212         }
00213 
00214         static private function unprefixTable( $tableName ) {
00215                 global $wgDBprefix;
00216                 return substr( $tableName, strlen( $wgDBprefix ) );
00217         }
00218 
00219         static private function isNotUnittest( $table ) {
00220                 return strpos( $table, 'unittest_' ) !== 0;
00221         }
00222 
00223         protected function listTables() {
00224                 global $wgDBprefix;
00225 
00226                 $tables = $this->db->listTables( $wgDBprefix, __METHOD__ );
00227                 $tables = array_map( array( __CLASS__, 'unprefixTable' ), $tables );
00228 
00229                 // Don't duplicate test tables from the previous fataled run
00230                 $tables = array_filter( $tables, array( __CLASS__, 'isNotUnittest' ) );
00231 
00232                 if ( $this->db->getType() == 'sqlite' ) {
00233                         $tables = array_flip( $tables );
00234                         // these are subtables of searchindex and don't need to be duped/dropped separately
00235                         unset( $tables['searchindex_content'] );
00236                         unset( $tables['searchindex_segdir'] );
00237                         unset( $tables['searchindex_segments'] );
00238                         $tables = array_flip( $tables );
00239                 }
00240                 return $tables;
00241         }
00242 
00243         protected function checkDbIsSupported() {
00244                 if( !in_array( $this->db->getType(), $this->supportedDBs ) ) {
00245                         throw new MWException( $this->db->getType() . " is not currently supported for unit testing." );
00246                 }
00247         }
00248 
00249         public function getCliArg( $offset ) {
00250 
00251                 if( isset( MediaWikiPHPUnitCommand::$additionalOptions[$offset] ) ) {
00252                         return MediaWikiPHPUnitCommand::$additionalOptions[$offset];
00253                 }
00254 
00255         }
00256 
00257         public function setCliArg( $offset, $value ) {
00258 
00259                 MediaWikiPHPUnitCommand::$additionalOptions[$offset] = $value;
00260 
00261         }
00262 
00263         public static function disableInterwikis( $prefix, &$data ) {
00264                 return false;
00265         }
00266 
00273         function hideDeprecated( $function ) {
00274                 wfSuppressWarnings();
00275                 wfDeprecated( $function );
00276                 wfRestoreWarnings();
00277         }
00278 }