public $regex = '';
public $runDisabled = false;
+ /**
+ * $called tracks whether the setUp and tearDown method has been called.
+ * class extending MediaWikiTestCase usually override setUp and tearDown
+ * but forget to call the parent.
+ *
+ * The array format takes a method name as key and anything as a value.
+ * By asserting the key exist, we know the child class has called the
+ * parent.
+ *
+ * This property must be private, we do not want child to override it,
+ * they should call the appropriate parent method instead.
+ */
+ private $called = array();
+
/**
* @var Array of TestUser
*/
$this->backupStaticAttributes = false;
}
- function run( PHPUnit_Framework_TestResult $result = NULL ) {
+ function run( PHPUnit_Framework_TestResult $result = null ) {
/* Some functions require some kind of caching, and will end up using the db,
* which we can't allow, as that would open a new connection for mysql.
* Replace with a HashBag. They would not be going to persist anyway.
*/
ObjectCache::$instances[CACHE_DB] = new HashBagOStuff;
+ $needsResetDB = false;
+ $logName = get_class( $this ) . '::' . $this->getName( false );
+
if( $this->needsDB() ) {
// set up a DB connection for this test to use
$this->checkDbIsSupported();
if( !self::$dbSetup ) {
+ wfProfileIn( $logName . ' (clone-db)' );
+
// switch to a temporary clone of the database
self::setupTestDB( $this->db, $this->dbPrefix() );
if ( ( $this->db->getType() == 'oracle' || !self::$useTemporaryTables ) && self::$reuseDB ) {
$this->resetDB();
}
+
+ wfProfileOut( $logName . ' (clone-db)' );
}
+ wfProfileIn( $logName . ' (prepare-db)' );
$this->addCoreDBData();
$this->addDBData();
+ wfProfileOut( $logName . ' (prepare-db)' );
- parent::run( $result );
+ $needsResetDB = true;
+ }
+ wfProfileIn( $logName );
+ parent::run( $result );
+ wfProfileOut( $logName );
+
+ if( $needsResetDB ) {
+ wfProfileIn( $logName . ' (reset-db)' );
$this->resetDB();
- } else {
- parent::run( $result );
+ wfProfileOut( $logName . ' (reset-db)' );
}
}
* happen in reverse order.
*/
protected function setUp() {
+ wfProfileIn( __METHOD__ );
parent::setUp();
+ $this->called['setUp'] = 1;
/*
//@todo: global variables to restore for *every* test
}
}
- // Clean up open transactions
if ( $this->needsDB() && $this->db ) {
+ // Clean up open transactions
while( $this->db->trxLevel() > 0 ) {
$this->db->rollback();
}
+
+ // don't ignore DB errors
+ $this->db->ignoreErrors( false );
}
+
+ wfProfileOut( __METHOD__ );
}
protected function tearDown() {
+ wfProfileIn( __METHOD__ );
+
// Cleaning up temporary files
foreach ( $this->tmpfiles as $fname ) {
if ( is_file( $fname ) || ( is_link( $fname ) ) ) {
}
}
- // Clean up open transactions
if ( $this->needsDB() && $this->db ) {
+ // Clean up open transactions
while( $this->db->trxLevel() > 0 ) {
$this->db->rollback();
}
+
+ // don't ignore DB errors
+ $this->db->ignoreErrors( false );
}
// Restore mw globals
$this->mwGlobals = array();
parent::tearDown();
+ wfProfileOut( __METHOD__ );
+ }
+
+ /**
+ * Make sure MediaWikiTestCase extending classes have called their
+ * parent setUp method
+ */
+ final public function testMediaWikiTestCaseParentSetupCalled() {
+ $this->assertArrayHasKey( 'setUp', $this->called,
+ get_called_class() . "::setUp() must call parent::setUp()"
+ );
}
/**
'page_id' => 0,
'page_namespace' => 0,
'page_title' => ' ',
- 'page_restrictions' => NULL,
+ 'page_restrictions' => null,
'page_counter' => 0,
'page_is_redirect' => 0,
'page_is_new' => 0,
return $this->assertTrue( $value == '', $msg );
}
- static private function unprefixTable( $tableName ) {
+ private static function unprefixTable( $tableName ) {
global $wgDBprefix;
return substr( $tableName, strlen( $wgDBprefix ) );
}
- static private function isNotUnittest( $table ) {
+ private static function isNotUnittest( $table ) {
return strpos( $table, 'unittest_' ) !== 0;
}
// But frequently, this is used in fixture setup.
throw new MWException( "No namespace defaults to wikitext!" );
}
+
+ /**
+ * Check, if $wgDiff3 is set and ready to merge
+ * Will mark the calling test as skipped, if not ready
+ *
+ * @since 1.21
+ */
+ protected function checkHasDiff3() {
+ global $wgDiff3;
+
+ # This check may also protect against code injection in
+ # case of broken installations.
+ wfSuppressWarnings();
+ $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
+ wfRestoreWarnings();
+
+ if( !$haveDiff3 ) {
+ $this->markTestSkipped( "Skip test, since diff3 is not configured" );
+ }
+ }
+
+ /**
+ * Check whether we have the 'gzip' commandline utility, will skip
+ * the test whenever "gzip -V" fails.
+ *
+ * Result is cached at the process level.
+ *
+ * @return bool
+ *
+ * @since 1.21
+ */
+ protected function checkHasGzip() {
+ static $haveGzip;
+
+ if( $haveGzip === null ) {
+ $retval = null;
+ wfShellExec( 'gzip -V', $retval );
+ $haveGzip = ($retval === 0);
+ }
+
+ if( !$haveGzip ) {
+ $this->markTestSkipped( "Skip test, requires the gzip utility in PATH" );
+ }
+
+ return $haveGzip;
+ }
+
+ /**
+ * Check if $extName is a loaded PHP extension, will skip the
+ * test whenever it is not loaded.
+ *
+ * @since 1.21
+ */
+ protected function checkPHPExtension( $extName ) {
+ $loaded = extension_loaded( $extName );
+ if( ! $loaded ) {
+ $this->markTestSkipped( "PHP extension '$extName' is not loaded, skipping." );
+ }
+ return $loaded;
+ }
+
+ /**
+ * Asserts that an exception of the specified type occurs when running
+ * the provided code.
+ *
+ * @since 1.21
+ *
+ * @param callable $code
+ * @param string $expected
+ * @param string $message
+ */
+ protected function assertException( $code, $expected = 'Exception', $message = '' ) {
+ $pokemons = null;
+
+ try {
+ call_user_func( $code );
+ }
+ catch ( Exception $pokemons ) {
+ // Gotta Catch 'Em All!
+ }
+
+ if ( $message === '' ) {
+ $message = 'An exception of type "' . $expected . '" should have been thrown';
+ }
+
+ $this->assertInstanceOf( $expected, $pokemons, $message );
+ }
+
}