Allow test to remove data from the database
[lhc/web/wiklou.git] / tests / phpunit / MediaWikiTestCase.php
1 <?php
2
3 abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
4 public $suite;
5 public $regex = '';
6 public $runDisabled = false;
7
8 /**
9 * @var DatabaseBase
10 */
11 protected $db;
12 protected $oldTablePrefix;
13 protected $useTemporaryTables = true;
14 private static $dbSetup = false;
15
16 /**
17 * Table name prefixes. Oracle likes it shorter.
18 */
19 const DB_PREFIX = 'unittest_';
20 const ORA_DB_PREFIX = 'ut_';
21
22 protected $supportedDBs = array(
23 'mysql',
24 'sqlite',
25 'oracle'
26 );
27
28 function __construct( $name = null, array $data = array(), $dataName = '' ) {
29 parent::__construct( $name, $data, $dataName );
30
31 $this->backupGlobals = false;
32 $this->backupStaticAttributes = false;
33 }
34
35 function run( PHPUnit_Framework_TestResult $result = NULL ) {
36 /* Some functions require some kind of caching, and will end up using the db,
37 * which we can't allow, as that would open a new connection for mysql.
38 * Replace with a HashBag. They would not be going to persist anyway.
39 */
40 ObjectCache::$instances[CACHE_DB] = new HashBagOStuff;
41
42 if( $this->needsDB() ) {
43
44 global $wgDBprefix;
45
46 $this->db = wfGetDB( DB_MASTER );
47
48 $this->checkDbIsSupported();
49
50 $this->oldTablePrefix = $wgDBprefix;
51
52 if( !self::$dbSetup ) {
53 $this->initDB();
54 self::$dbSetup = true;
55 }
56
57 $this->resetDB();
58 $this->addCoreDBData();
59 $this->addDBData();
60
61 parent::run( $result );
62
63 $this->removeDBData();
64
65 } else {
66 parent::run( $result );
67 }
68 }
69
70 function __destruct() {
71 if( $this->needsDB() ) {
72 $this->destroyDB();
73 }
74 }
75
76 function dbPrefix() {
77 return $this->db->getType() == 'oracle' ? self::ORA_DB_PREFIX : self::DB_PREFIX;
78 }
79
80 function needsDB() {
81 $rc = new ReflectionClass( $this );
82 return strpos( $rc->getDocComment(), '@group Database' ) !== false;
83 }
84
85 /**
86 * Stub. If a test needs to add additional data to the database, it should
87 * implement this method and do so
88 */
89 function addDBData() {}
90
91 /**
92 * Stub. If a test needs to remove data from the database. Called after
93 * test run.
94 */
95 function removeDBData() {}
96
97 private function addCoreDBData() {
98
99 User::resetIdByNameCache();
100
101 //Make sysop user
102 $user = User::newFromName( 'UTSysop' );
103
104 if ( $user->idForName() == 0 ) {
105 $user->addToDatabase();
106 $user->setPassword( 'UTSysopPassword' );
107
108 $user->addGroup( 'sysop' );
109 $user->addGroup( 'bureaucrat' );
110 $user->saveSettings();
111 }
112
113
114 //Make 1 page with 1 revision
115 $article = new Article( Title::newFromText( 'UTPage' ) );
116 $article->doEdit( 'UTContent',
117 'UTPageSummary',
118 EDIT_NEW,
119 false,
120 User::newFromName( 'UTSysop' ) );
121 }
122
123 private function initDB() {
124 global $wgDBprefix;
125 if ( $wgDBprefix === $this->dbPrefix() ) {
126 throw new MWException( 'Cannot run unit tests, the database prefix is already "unittest_"' );
127 }
128
129 $dbClone = new CloneDatabase( $this->db, $this->listTables(), $this->dbPrefix() );
130 $dbClone->useTemporaryTables( $this->useTemporaryTables );
131 $dbClone->cloneTableStructure();
132 }
133
134 /**
135 * Empty all tables so they can be repopulated for tests
136 */
137 private function resetDB() {
138 if( $this->db ) {
139 foreach( $this->listTables() as $tbl ) {
140 $this->db->delete( $tbl, '*', __METHOD__ );
141 }
142
143 if ( $this->db->getType() == 'oracle' ) {
144 $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
145
146 # Insert 0 user to prevent FK violations
147 # Anonymous user
148 $this->db->insert( 'user', array(
149 'user_id' => 0,
150 'user_name' => 'Anonymous' ) );
151 }
152 }
153 }
154
155 protected function destroyDB() {
156
157 if ( $this->useTemporaryTables ) {
158 # Don't need to do anything
159 //return;
160 //Temporary tables seem to be broken ATM, delete anyway
161 }
162
163 if( is_null( $this->db ) ) {
164 return;
165 }
166
167 $tables = $this->db->listTables( $this->dbPrefix(), __METHOD__ );
168
169 foreach ( $tables as $table ) {
170 try {
171 $sql = $this->db->getType() == 'oracle' ? "DROP TABLE $table CASCADE CONSTRAINTS PURGE" : "DROP TABLE `$table`";
172 $this->db->query( $sql, __METHOD__ );
173 } catch( Exception $e ) {
174 }
175 }
176
177 if ( $this->db->getType() == 'oracle' )
178 $this->db->query( 'BEGIN FILL_WIKI_INFO; END;', __METHOD__ );
179
180 CloneDatabase::changePrefix( $this->oldTablePrefix );
181 }
182
183 function __call( $func, $args ) {
184 static $compatibility = array(
185 'assertInternalType' => 'assertType',
186 'assertNotInternalType' => 'assertNotType',
187 'assertInstanceOf' => 'assertType',
188 'assertEmpty' => 'assertEmpty2',
189 );
190
191 if ( method_exists( $this->suite, $func ) ) {
192 return call_user_func_array( array( $this->suite, $func ), $args);
193 } elseif ( isset( $compatibility[$func] ) ) {
194 return call_user_func_array( array( $this, $compatibility[$func] ), $args);
195 } else {
196 throw new MWException( "Called non-existant $func method on "
197 . get_class( $this ) );
198 }
199 }
200
201 private function assertEmpty2( $value, $msg ) {
202 return $this->assertTrue( $value == '', $msg );
203 }
204
205 static private function unprefixTable( $tableName ) {
206 global $wgDBprefix;
207 return substr( $tableName, strlen( $wgDBprefix ) );
208 }
209
210 protected function listTables() {
211 global $wgDBprefix;
212
213 $tables = $this->db->listTables( $wgDBprefix, __METHOD__ );
214 $tables = array_map( array( __CLASS__, 'unprefixTable' ), $tables );
215
216 if ( $this->db->getType() == 'sqlite' ) {
217 $tables = array_flip( $tables );
218 // these are subtables of searchindex and don't need to be duped/dropped separately
219 unset( $tables['searchindex_content'] );
220 unset( $tables['searchindex_segdir'] );
221 unset( $tables['searchindex_segments'] );
222 $tables = array_flip( $tables );
223 }
224 return $tables;
225
226 }
227
228 protected function checkDbIsSupported() {
229 if( !in_array( $this->db->getType(), $this->supportedDBs ) ) {
230 throw new MWException( $this->db->getType() . " is not currently supported for unit testing." );
231 }
232 }
233
234 public function getCliArg( $offset ) {
235
236 if( isset( MediaWikiPHPUnitCommand::$additionalOptions[$offset] ) ) {
237 return MediaWikiPHPUnitCommand::$additionalOptions[$offset];
238 }
239
240 }
241
242 public function setCliArg( $offset, $value ) {
243
244 MediaWikiPHPUnitCommand::$additionalOptions[$offset] = $value;
245
246 }
247 }
248