[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Advanced test case. 19 * 20 * @package core 21 * @category phpunit 22 * @copyright 2012 Petr Skoda {@link http://skodak.org} 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 27 /** 28 * Advanced PHPUnit test case customised for Moodle. 29 * 30 * @package core 31 * @category phpunit 32 * @copyright 2012 Petr Skoda {@link http://skodak.org} 33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 */ 35 abstract class advanced_testcase extends PHPUnit_Framework_TestCase { 36 /** @var bool automatically reset everything? null means log changes */ 37 private $resetAfterTest; 38 39 /** @var moodle_transaction */ 40 private $testdbtransaction; 41 42 /** @var int timestamp used for current time asserts */ 43 private $currenttimestart; 44 45 /** 46 * Constructs a test case with the given name. 47 * 48 * Note: use setUp() or setUpBeforeClass() in your test cases. 49 * 50 * @param string $name 51 * @param array $data 52 * @param string $dataName 53 */ 54 final public function __construct($name = null, array $data = array(), $dataName = '') { 55 parent::__construct($name, $data, $dataName); 56 57 $this->setBackupGlobals(false); 58 $this->setBackupStaticAttributes(false); 59 $this->setRunTestInSeparateProcess(false); 60 } 61 62 /** 63 * Runs the bare test sequence. 64 * @return void 65 */ 66 final public function runBare() { 67 global $DB; 68 69 if (phpunit_util::$lastdbwrites != $DB->perf_get_writes()) { 70 // this happens when previous test does not reset, we can not use transactions 71 $this->testdbtransaction = null; 72 73 } else if ($DB->get_dbfamily() === 'postgres' or $DB->get_dbfamily() === 'mssql') { 74 // database must allow rollback of DDL, so no mysql here 75 $this->testdbtransaction = $DB->start_delegated_transaction(); 76 } 77 78 try { 79 $this->setCurrentTimeStart(); 80 parent::runBare(); 81 // set DB reference in case somebody mocked it in test 82 $DB = phpunit_util::get_global_backup('DB'); 83 84 // Deal with any debugging messages. 85 $debugerror = phpunit_util::display_debugging_messages(); 86 phpunit_util::reset_debugging(); 87 if ($debugerror) { 88 trigger_error('Unenxpected debugging() call detected.', E_USER_NOTICE); 89 } 90 91 } catch (Exception $e) { 92 // cleanup after failed expectation 93 phpunit_util::reset_all_data(); 94 throw $e; 95 } 96 97 if (!$this->testdbtransaction or $this->testdbtransaction->is_disposed()) { 98 $this->testdbtransaction = null; 99 } 100 101 if ($this->resetAfterTest === true) { 102 if ($this->testdbtransaction) { 103 $DB->force_transaction_rollback(); 104 phpunit_util::reset_all_database_sequences(); 105 phpunit_util::$lastdbwrites = $DB->perf_get_writes(); // no db reset necessary 106 } 107 phpunit_util::reset_all_data(null); 108 109 } else if ($this->resetAfterTest === false) { 110 if ($this->testdbtransaction) { 111 $this->testdbtransaction->allow_commit(); 112 } 113 // keep all data untouched for other tests 114 115 } else { 116 // reset but log what changed 117 if ($this->testdbtransaction) { 118 try { 119 $this->testdbtransaction->allow_commit(); 120 } catch (dml_transaction_exception $e) { 121 phpunit_util::reset_all_data(); 122 throw new coding_exception('Invalid transaction state detected in test '.$this->getName()); 123 } 124 } 125 phpunit_util::reset_all_data(true); 126 } 127 128 // make sure test did not forget to close transaction 129 if ($DB->is_transaction_started()) { 130 phpunit_util::reset_all_data(); 131 if ($this->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED 132 or $this->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED 133 or $this->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE) { 134 throw new coding_exception('Test '.$this->getName().' did not close database transaction'); 135 } 136 } 137 } 138 139 /** 140 * Creates a new FlatXmlDataSet with the given $xmlFile. (absolute path.) 141 * 142 * @param string $xmlFile 143 * @return PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet 144 */ 145 protected function createFlatXMLDataSet($xmlFile) { 146 return new PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet($xmlFile); 147 } 148 149 /** 150 * Creates a new XMLDataSet with the given $xmlFile. (absolute path.) 151 * 152 * @param string $xmlFile 153 * @return PHPUnit_Extensions_Database_DataSet_XmlDataSet 154 */ 155 protected function createXMLDataSet($xmlFile) { 156 return new PHPUnit_Extensions_Database_DataSet_XmlDataSet($xmlFile); 157 } 158 159 /** 160 * Creates a new CsvDataSet from the given array of csv files. (absolute paths.) 161 * 162 * @param array $files array tablename=>cvsfile 163 * @param string $delimiter 164 * @param string $enclosure 165 * @param string $escape 166 * @return PHPUnit_Extensions_Database_DataSet_CsvDataSet 167 */ 168 protected function createCsvDataSet($files, $delimiter = ',', $enclosure = '"', $escape = '"') { 169 $dataSet = new PHPUnit_Extensions_Database_DataSet_CsvDataSet($delimiter, $enclosure, $escape); 170 foreach($files as $table=>$file) { 171 $dataSet->addTable($table, $file); 172 } 173 return $dataSet; 174 } 175 176 /** 177 * Creates new ArrayDataSet from given array 178 * 179 * @param array $data array of tables, first row in each table is columns 180 * @return phpunit_ArrayDataSet 181 */ 182 protected function createArrayDataSet(array $data) { 183 return new phpunit_ArrayDataSet($data); 184 } 185 186 /** 187 * Load date into moodle database tables from standard PHPUnit data set. 188 * 189 * Note: it is usually better to use data generators 190 * 191 * @param PHPUnit_Extensions_Database_DataSet_IDataSet $dataset 192 * @return void 193 */ 194 protected function loadDataSet(PHPUnit_Extensions_Database_DataSet_IDataSet $dataset) { 195 global $DB; 196 197 $structure = phpunit_util::get_tablestructure(); 198 199 foreach($dataset->getTableNames() as $tablename) { 200 $table = $dataset->getTable($tablename); 201 $metadata = $dataset->getTableMetaData($tablename); 202 $columns = $metadata->getColumns(); 203 204 $doimport = false; 205 if (isset($structure[$tablename]['id']) and $structure[$tablename]['id']->auto_increment) { 206 $doimport = in_array('id', $columns); 207 } 208 209 for($r=0; $r<$table->getRowCount(); $r++) { 210 $record = $table->getRow($r); 211 if ($doimport) { 212 $DB->import_record($tablename, $record); 213 } else { 214 $DB->insert_record($tablename, $record); 215 } 216 } 217 if ($doimport) { 218 $DB->get_manager()->reset_sequence(new xmldb_table($tablename)); 219 } 220 } 221 } 222 223 /** 224 * Call this method from test if you want to make sure that 225 * the resetting of database is done the slow way without transaction 226 * rollback. 227 * 228 * This is useful especially when testing stuff that is not compatible with transactions. 229 * 230 * @return void 231 */ 232 public function preventResetByRollback() { 233 if ($this->testdbtransaction and !$this->testdbtransaction->is_disposed()) { 234 $this->testdbtransaction->allow_commit(); 235 $this->testdbtransaction = null; 236 } 237 } 238 239 /** 240 * Reset everything after current test. 241 * @param bool $reset true means reset state back, false means keep all data for the next test, 242 * null means reset state and show warnings if anything changed 243 * @return void 244 */ 245 public function resetAfterTest($reset = true) { 246 $this->resetAfterTest = $reset; 247 } 248 249 /** 250 * Return debugging messages from the current test. 251 * @return array with instances having 'message', 'level' and 'stacktrace' property. 252 */ 253 public function getDebuggingMessages() { 254 return phpunit_util::get_debugging_messages(); 255 } 256 257 /** 258 * Clear all previous debugging messages in current test 259 * and revert to default DEVELOPER_DEBUG level. 260 */ 261 public function resetDebugging() { 262 phpunit_util::reset_debugging(); 263 } 264 265 /** 266 * Assert that exactly debugging was just called once. 267 * 268 * Discards the debugging message if successful. 269 * 270 * @param null|string $debugmessage null means any 271 * @param null|string $debuglevel null means any 272 * @param string $message 273 */ 274 public function assertDebuggingCalled($debugmessage = null, $debuglevel = null, $message = '') { 275 $debugging = phpunit_util::get_debugging_messages(); 276 $count = count($debugging); 277 278 if ($count == 0) { 279 if ($message === '') { 280 $message = 'Expectation failed, debugging() not triggered.'; 281 } 282 $this->fail($message); 283 } 284 if ($count > 1) { 285 if ($message === '') { 286 $message = 'Expectation failed, debugging() triggered '.$count.' times.'; 287 } 288 $this->fail($message); 289 } 290 $this->assertEquals(1, $count); 291 292 $debug = reset($debugging); 293 if ($debugmessage !== null) { 294 $this->assertSame($debugmessage, $debug->message, $message); 295 } 296 if ($debuglevel !== null) { 297 $this->assertSame($debuglevel, $debug->level, $message); 298 } 299 300 phpunit_util::reset_debugging(); 301 } 302 303 /** 304 * Call when no debugging() messages expected. 305 * @param string $message 306 */ 307 public function assertDebuggingNotCalled($message = '') { 308 $debugging = phpunit_util::get_debugging_messages(); 309 $count = count($debugging); 310 311 if ($message === '') { 312 $message = 'Expectation failed, debugging() was triggered.'; 313 } 314 $this->assertEquals(0, $count, $message); 315 } 316 317 /** 318 * Assert that an event legacy data is equal to the expected value. 319 * 320 * @param mixed $expected expected data. 321 * @param \core\event\base $event the event object. 322 * @param string $message 323 * @return void 324 */ 325 public function assertEventLegacyData($expected, \core\event\base $event, $message = '') { 326 $legacydata = phpunit_event_mock::testable_get_legacy_eventdata($event); 327 if ($message === '') { 328 $message = 'Event legacy data does not match expected value.'; 329 } 330 $this->assertEquals($expected, $legacydata, $message); 331 } 332 333 /** 334 * Assert that an event legacy log data is equal to the expected value. 335 * 336 * @param mixed $expected expected data. 337 * @param \core\event\base $event the event object. 338 * @param string $message 339 * @return void 340 */ 341 public function assertEventLegacyLogData($expected, \core\event\base $event, $message = '') { 342 $legacydata = phpunit_event_mock::testable_get_legacy_logdata($event); 343 if ($message === '') { 344 $message = 'Event legacy log data does not match expected value.'; 345 } 346 $this->assertEquals($expected, $legacydata, $message); 347 } 348 349 /** 350 * Assert that an event is not using event->contxet. 351 * While restoring context might not be valid and it should not be used by event url 352 * or description methods. 353 * 354 * @param \core\event\base $event the event object. 355 * @param string $message 356 * @return void 357 */ 358 public function assertEventContextNotUsed(\core\event\base $event, $message = '') { 359 // Save current event->context and set it to false. 360 $eventcontext = phpunit_event_mock::testable_get_event_context($event); 361 phpunit_event_mock::testable_set_event_context($event, false); 362 if ($message === '') { 363 $message = 'Event should not use context property of event in any method.'; 364 } 365 366 // Test event methods should not use event->context. 367 $event->get_url(); 368 $event->get_description(); 369 $event->get_legacy_eventname(); 370 phpunit_event_mock::testable_get_legacy_eventdata($event); 371 phpunit_event_mock::testable_get_legacy_logdata($event); 372 373 // Restore event->context. 374 phpunit_event_mock::testable_set_event_context($event, $eventcontext); 375 } 376 377 /** 378 * Stores current time as the base for assertTimeCurrent(). 379 * 380 * Note: this is called automatically before calling individual test methods. 381 * @return int current time 382 */ 383 public function setCurrentTimeStart() { 384 $this->currenttimestart = time(); 385 return $this->currenttimestart; 386 } 387 388 /** 389 * Assert that: start < $time < time() 390 * @param int $time 391 * @param string $message 392 * @return void 393 */ 394 public function assertTimeCurrent($time, $message = '') { 395 $msg = ($message === '') ? 'Time is lower that allowed start value' : $message; 396 $this->assertGreaterThanOrEqual($this->currenttimestart, $time, $msg); 397 $msg = ($message === '') ? 'Time is in the future' : $message; 398 $this->assertLessThanOrEqual(time(), $time, $msg); 399 } 400 401 /** 402 * Starts message redirection. 403 * 404 * You can verify if messages were sent or not by inspecting the messages 405 * array in the returned messaging sink instance. The redirection 406 * can be stopped by calling $sink->close(); 407 * 408 * @return phpunit_message_sink 409 */ 410 public function redirectMessages() { 411 return phpunit_util::start_message_redirection(); 412 } 413 414 /** 415 * Starts email redirection. 416 * 417 * You can verify if email were sent or not by inspecting the email 418 * array in the returned phpmailer sink instance. The redirection 419 * can be stopped by calling $sink->close(); 420 * 421 * @return phpunit_message_sink 422 */ 423 public function redirectEmails() { 424 return phpunit_util::start_phpmailer_redirection(); 425 } 426 427 /** 428 * Starts event redirection. 429 * 430 * You can verify if events were triggered or not by inspecting the events 431 * array in the returned event sink instance. The redirection 432 * can be stopped by calling $sink->close(); 433 * 434 * @return phpunit_event_sink 435 */ 436 public function redirectEvents() { 437 return phpunit_util::start_event_redirection(); 438 } 439 440 /** 441 * Cleanup after all tests are executed. 442 * 443 * Note: do not forget to call this if overridden... 444 * 445 * @static 446 * @return void 447 */ 448 public static function tearDownAfterClass() { 449 phpunit_util::reset_all_data(); 450 } 451 452 /** 453 * Reset all database tables, restore global state and clear caches and optionally purge dataroot dir. 454 * @static 455 * @return void 456 */ 457 public static function resetAllData() { 458 phpunit_util::reset_all_data(); 459 } 460 461 /** 462 * Set current $USER, reset access cache. 463 * @static 464 * @param null|int|stdClass $user user record, null or 0 means non-logged-in, positive integer means userid 465 * @return void 466 */ 467 public static function setUser($user = null) { 468 global $CFG, $DB; 469 470 if (is_object($user)) { 471 $user = clone($user); 472 } else if (!$user) { 473 $user = new stdClass(); 474 $user->id = 0; 475 $user->mnethostid = $CFG->mnet_localhost_id; 476 } else { 477 $user = $DB->get_record('user', array('id'=>$user)); 478 } 479 unset($user->description); 480 unset($user->access); 481 unset($user->preference); 482 483 \core\session\manager::set_user($user); 484 } 485 486 /** 487 * Set current $USER to admin account, reset access cache. 488 * @static 489 * @return void 490 */ 491 public static function setAdminUser() { 492 self::setUser(2); 493 } 494 495 /** 496 * Set current $USER to guest account, reset access cache. 497 * @static 498 * @return void 499 */ 500 public static function setGuestUser() { 501 self::setUser(1); 502 } 503 504 /** 505 * Get data generator 506 * @static 507 * @return testing_data_generator 508 */ 509 public static function getDataGenerator() { 510 return phpunit_util::get_data_generator(); 511 } 512 513 /** 514 * Returns UTL of the external test file. 515 * 516 * The result depends on the value of following constants: 517 * - TEST_EXTERNAL_FILES_HTTP_URL 518 * - TEST_EXTERNAL_FILES_HTTPS_URL 519 * 520 * They should point to standard external test files repository, 521 * it defaults to 'http://download.moodle.org/unittest'. 522 * 523 * False value means skip tests that require external files. 524 * 525 * @param string $path 526 * @param bool $https true if https required 527 * @return string url 528 */ 529 public function getExternalTestFileUrl($path, $https = false) { 530 $path = ltrim($path, '/'); 531 if ($path) { 532 $path = '/'.$path; 533 } 534 if ($https) { 535 if (defined('TEST_EXTERNAL_FILES_HTTPS_URL')) { 536 if (!TEST_EXTERNAL_FILES_HTTPS_URL) { 537 $this->markTestSkipped('Tests using external https test files are disabled'); 538 } 539 return TEST_EXTERNAL_FILES_HTTPS_URL.$path; 540 } 541 return 'https://download.moodle.org/unittest'.$path; 542 } 543 544 if (defined('TEST_EXTERNAL_FILES_HTTP_URL')) { 545 if (!TEST_EXTERNAL_FILES_HTTP_URL) { 546 $this->markTestSkipped('Tests using external http test files are disabled'); 547 } 548 return TEST_EXTERNAL_FILES_HTTP_URL.$path; 549 } 550 return 'http://download.moodle.org/unittest'.$path; 551 } 552 553 /** 554 * Recursively visit all the files in the source tree. Calls the callback 555 * function with the pathname of each file found. 556 * 557 * @param string $path the folder to start searching from. 558 * @param string $callback the method of this class to call with the name of each file found. 559 * @param string $fileregexp a regexp used to filter the search (optional). 560 * @param bool $exclude If true, pathnames that match the regexp will be ignored. If false, 561 * only files that match the regexp will be included. (default false). 562 * @param array $ignorefolders will not go into any of these folders (optional). 563 * @return void 564 */ 565 public function recurseFolders($path, $callback, $fileregexp = '/.*/', $exclude = false, $ignorefolders = array()) { 566 $files = scandir($path); 567 568 foreach ($files as $file) { 569 $filepath = $path .'/'. $file; 570 if (strpos($file, '.') === 0) { 571 /// Don't check hidden files. 572 continue; 573 } else if (is_dir($filepath)) { 574 if (!in_array($filepath, $ignorefolders)) { 575 $this->recurseFolders($filepath, $callback, $fileregexp, $exclude, $ignorefolders); 576 } 577 } else if ($exclude xor preg_match($fileregexp, $filepath)) { 578 $this->$callback($filepath); 579 } 580 } 581 } 582 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |