[ 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 * Testing util classes 19 * 20 * @abstract 21 * @package core 22 * @category test 23 * @copyright 2012 Petr Skoda {@link http://skodak.org} 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 27 /** 28 * Utils for test sites creation 29 * 30 * @package core 31 * @category test 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 testing_util { 36 37 /** 38 * @var string dataroot (likely to be $CFG->dataroot). 39 */ 40 private static $dataroot = null; 41 42 /** 43 * @var testing_data_generator 44 */ 45 protected static $generator = null; 46 47 /** 48 * @var string current version hash from php files 49 */ 50 protected static $versionhash = null; 51 52 /** 53 * @var array original content of all database tables 54 */ 55 protected static $tabledata = null; 56 57 /** 58 * @var array original structure of all database tables 59 */ 60 protected static $tablestructure = null; 61 62 /** 63 * @var array original structure of all database tables 64 */ 65 protected static $sequencenames = null; 66 67 /** 68 * @var string name of the json file where we store the list of dataroot files to not reset during reset_dataroot. 69 */ 70 private static $originaldatafilesjson = 'originaldatafiles.json'; 71 72 /** 73 * @var boolean set to true once $originaldatafilesjson file is created. 74 */ 75 private static $originaldatafilesjsonadded = false; 76 77 /** 78 * @var int next sequence value for a single test cycle. 79 */ 80 protected static $sequencenextstartingid = null; 81 /** 82 * Return the name of the JSON file containing the init filenames. 83 * 84 * @static 85 * @return string 86 */ 87 public static function get_originaldatafilesjson() { 88 return self::$originaldatafilesjson; 89 } 90 91 /** 92 * Return the dataroot. It's useful when mocking the dataroot when unit testing this class itself. 93 * 94 * @static 95 * @return string the dataroot. 96 */ 97 public static function get_dataroot() { 98 global $CFG; 99 100 // By default it's the test framework dataroot. 101 if (empty(self::$dataroot)) { 102 self::$dataroot = $CFG->dataroot; 103 } 104 105 return self::$dataroot; 106 } 107 108 /** 109 * Set the dataroot. It's useful when mocking the dataroot when unit testing this class itself. 110 * 111 * @param string $dataroot the dataroot of the test framework. 112 * @static 113 */ 114 public static function set_dataroot($dataroot) { 115 self::$dataroot = $dataroot; 116 } 117 118 /** 119 * Returns the testing framework name 120 * @static 121 * @return string 122 */ 123 protected static final function get_framework() { 124 $classname = get_called_class(); 125 return substr($classname, 0, strpos($classname, '_')); 126 } 127 128 /** 129 * Get data generator 130 * @static 131 * @return testing_data_generator 132 */ 133 public static function get_data_generator() { 134 if (is_null(self::$generator)) { 135 require_once (__DIR__.'/../generator/lib.php'); 136 self::$generator = new testing_data_generator(); 137 } 138 return self::$generator; 139 } 140 141 /** 142 * Does this site (db and dataroot) appear to be used for production? 143 * We try very hard to prevent accidental damage done to production servers!! 144 * 145 * @static 146 * @return bool 147 */ 148 public static function is_test_site() { 149 global $DB, $CFG; 150 151 $framework = self::get_framework(); 152 153 if (!file_exists(self::get_dataroot() . '/' . $framework . 'testdir.txt')) { 154 // this is already tested in bootstrap script, 155 // but anyway presence of this file means the dataroot is for testing 156 return false; 157 } 158 159 $tables = $DB->get_tables(false); 160 if ($tables) { 161 if (!$DB->get_manager()->table_exists('config')) { 162 return false; 163 } 164 if (!get_config('core', $framework . 'test')) { 165 return false; 166 } 167 } 168 169 return true; 170 } 171 172 /** 173 * Returns whether test database and dataroot were created using the current version codebase 174 * 175 * @return bool 176 */ 177 public static function is_test_data_updated() { 178 global $CFG; 179 180 $framework = self::get_framework(); 181 182 $datarootpath = self::get_dataroot() . '/' . $framework; 183 if (!file_exists($datarootpath . '/tabledata.ser') or !file_exists($datarootpath . '/tablestructure.ser')) { 184 return false; 185 } 186 187 if (!file_exists($datarootpath . '/versionshash.txt')) { 188 return false; 189 } 190 191 $hash = core_component::get_all_versions_hash(); 192 $oldhash = file_get_contents($datarootpath . '/versionshash.txt'); 193 194 if ($hash !== $oldhash) { 195 return false; 196 } 197 198 $dbhash = get_config('core', $framework . 'test'); 199 if ($hash !== $dbhash) { 200 return false; 201 } 202 203 return true; 204 } 205 206 /** 207 * Stores the status of the database 208 * 209 * Serializes the contents and the structure and 210 * stores it in the test framework space in dataroot 211 */ 212 protected static function store_database_state() { 213 global $DB, $CFG; 214 215 $framework = self::get_framework(); 216 217 // store data for all tables 218 $data = array(); 219 $structure = array(); 220 $tables = $DB->get_tables(); 221 foreach ($tables as $table) { 222 $columns = $DB->get_columns($table); 223 $structure[$table] = $columns; 224 if (isset($columns['id']) and $columns['id']->auto_increment) { 225 $data[$table] = $DB->get_records($table, array(), 'id ASC'); 226 } else { 227 // there should not be many of these 228 $data[$table] = $DB->get_records($table, array()); 229 } 230 } 231 $data = serialize($data); 232 $datafile = self::get_dataroot() . '/' . $framework . '/tabledata.ser'; 233 file_put_contents($datafile, $data); 234 testing_fix_file_permissions($datafile); 235 236 $structure = serialize($structure); 237 $structurefile = self::get_dataroot() . '/' . $framework . '/tablestructure.ser'; 238 file_put_contents($structurefile, $structure); 239 testing_fix_file_permissions($structurefile); 240 } 241 242 /** 243 * Stores the version hash in both database and dataroot 244 */ 245 protected static function store_versions_hash() { 246 global $CFG; 247 248 $framework = self::get_framework(); 249 $hash = core_component::get_all_versions_hash(); 250 251 // add test db flag 252 set_config($framework . 'test', $hash); 253 254 // hash all plugin versions - helps with very fast detection of db structure changes 255 $hashfile = self::get_dataroot() . '/' . $framework . '/versionshash.txt'; 256 file_put_contents($hashfile, $hash); 257 testing_fix_file_permissions($hashfile); 258 } 259 260 /** 261 * Returns contents of all tables right after installation. 262 * @static 263 * @return array $table=>$records 264 */ 265 protected static function get_tabledata() { 266 global $CFG; 267 268 $framework = self::get_framework(); 269 270 $datafile = self::get_dataroot() . '/' . $framework . '/tabledata.ser'; 271 if (!file_exists($datafile)) { 272 // Not initialised yet. 273 return array(); 274 } 275 276 if (!isset(self::$tabledata)) { 277 $data = file_get_contents($datafile); 278 self::$tabledata = unserialize($data); 279 } 280 281 if (!is_array(self::$tabledata)) { 282 testing_error(1, 'Can not read dataroot/' . $framework . '/tabledata.ser or invalid format, reinitialize test database.'); 283 } 284 285 return self::$tabledata; 286 } 287 288 /** 289 * Returns structure of all tables right after installation. 290 * @static 291 * @return array $table=>$records 292 */ 293 public static function get_tablestructure() { 294 global $CFG; 295 296 $framework = self::get_framework(); 297 298 $structurefile = self::get_dataroot() . '/' . $framework . '/tablestructure.ser'; 299 if (!file_exists($structurefile)) { 300 // Not initialised yet. 301 return array(); 302 } 303 304 if (!isset(self::$tablestructure)) { 305 $data = file_get_contents($structurefile); 306 self::$tablestructure = unserialize($data); 307 } 308 309 if (!is_array(self::$tablestructure)) { 310 testing_error(1, 'Can not read dataroot/' . $framework . '/tablestructure.ser or invalid format, reinitialize test database.'); 311 } 312 313 return self::$tablestructure; 314 } 315 316 /** 317 * Returns the names of sequences for each autoincrementing id field in all standard tables. 318 * @static 319 * @return array $table=>$sequencename 320 */ 321 public static function get_sequencenames() { 322 global $DB; 323 324 if (isset(self::$sequencenames)) { 325 return self::$sequencenames; 326 } 327 328 if (!$structure = self::get_tablestructure()) { 329 return array(); 330 } 331 332 self::$sequencenames = array(); 333 foreach ($structure as $table => $ignored) { 334 $name = $DB->get_manager()->generator->getSequenceFromDB(new xmldb_table($table)); 335 if ($name !== false) { 336 self::$sequencenames[$table] = $name; 337 } 338 } 339 340 return self::$sequencenames; 341 } 342 343 /** 344 * Returns list of tables that are unmodified and empty. 345 * 346 * @static 347 * @return array of table names, empty if unknown 348 */ 349 protected static function guess_unmodified_empty_tables() { 350 global $DB; 351 352 $dbfamily = $DB->get_dbfamily(); 353 354 if ($dbfamily === 'mysql') { 355 $empties = array(); 356 $prefix = $DB->get_prefix(); 357 $rs = $DB->get_recordset_sql("SHOW TABLE STATUS LIKE ?", array($prefix.'%')); 358 foreach ($rs as $info) { 359 $table = strtolower($info->name); 360 if (strpos($table, $prefix) !== 0) { 361 // incorrect table match caused by _ 362 continue; 363 } 364 if (!is_null($info->auto_increment)) { 365 $table = preg_replace('/^'.preg_quote($prefix, '/').'/', '', $table); 366 if ($info->auto_increment == 1) { 367 $empties[$table] = $table; 368 } 369 } 370 } 371 $rs->close(); 372 return $empties; 373 374 } else if ($dbfamily === 'mssql') { 375 $empties = array(); 376 $prefix = $DB->get_prefix(); 377 $sql = "SELECT t.name 378 FROM sys.identity_columns i 379 JOIN sys.tables t ON t.object_id = i.object_id 380 WHERE t.name LIKE ? 381 AND i.name = 'id' 382 AND i.last_value IS NULL"; 383 $rs = $DB->get_recordset_sql($sql, array($prefix.'%')); 384 foreach ($rs as $info) { 385 $table = strtolower($info->name); 386 if (strpos($table, $prefix) !== 0) { 387 // incorrect table match caused by _ 388 continue; 389 } 390 $table = preg_replace('/^'.preg_quote($prefix, '/').'/', '', $table); 391 $empties[$table] = $table; 392 } 393 $rs->close(); 394 return $empties; 395 396 } else if ($dbfamily === 'oracle') { 397 $sequences = self::get_sequencenames(); 398 $sequences = array_map('strtoupper', $sequences); 399 $lookup = array_flip($sequences); 400 $empties = array(); 401 list($seqs, $params) = $DB->get_in_or_equal($sequences); 402 $sql = "SELECT sequence_name FROM user_sequences WHERE last_number = 1 AND sequence_name $seqs"; 403 $rs = $DB->get_recordset_sql($sql, $params); 404 foreach ($rs as $seq) { 405 $table = $lookup[$seq->sequence_name]; 406 $empties[$table] = $table; 407 } 408 $rs->close(); 409 return $empties; 410 411 } else { 412 return array(); 413 } 414 } 415 416 /** 417 * Determine the next unique starting id sequences. 418 * 419 * @static 420 * @param array $records The records to use to determine the starting value for the table. 421 * @return int The value the sequence should be set to. 422 */ 423 private static function get_next_sequence_starting_value($records) { 424 $id = self::$sequencenextstartingid; 425 426 // If there are records, calculate the minimum id we can use. 427 // It must be bigger than the last record's id. 428 if (!empty($records)) { 429 $lastrecord = end($records); 430 $id = max($id, $lastrecord->id + 1); 431 } 432 433 self::$sequencenextstartingid = $id + 1000; 434 return $id; 435 } 436 437 /** 438 * Reset all database sequences to initial values. 439 * 440 * @static 441 * @param array $empties tables that are known to be unmodified and empty 442 * @return void 443 */ 444 public static function reset_all_database_sequences(array $empties = null) { 445 global $DB; 446 447 if (!$data = self::get_tabledata()) { 448 // Not initialised yet. 449 return; 450 } 451 if (!$structure = self::get_tablestructure()) { 452 // Not initialised yet. 453 return; 454 } 455 456 // If all starting Id's are the same, it's difficult to detect coding and testing 457 // errors that use the incorrect id in tests. The classic case is cmid vs instance id. 458 // To reduce the chance of the coding error, we start sequences at different values where possible. 459 // In a attempt to avoid tables with existing id's we start at a high number. 460 // Reset the value each time all database sequences are reset. 461 if (defined('PHPUNIT_SEQUENCE_START') and PHPUNIT_SEQUENCE_START) { 462 self::$sequencenextstartingid = PHPUNIT_SEQUENCE_START; 463 } else { 464 self::$sequencenextstartingid = 100000; 465 } 466 467 $dbfamily = $DB->get_dbfamily(); 468 if ($dbfamily === 'postgres') { 469 $queries = array(); 470 $prefix = $DB->get_prefix(); 471 foreach ($data as $table => $records) { 472 if (isset($structure[$table]['id']) and $structure[$table]['id']->auto_increment) { 473 $nextid = self::get_next_sequence_starting_value($records); 474 $queries[] = "ALTER SEQUENCE {$prefix}{$table}_id_seq RESTART WITH $nextid"; 475 } 476 } 477 if ($queries) { 478 $DB->change_database_structure(implode(';', $queries)); 479 } 480 481 } else if ($dbfamily === 'mysql') { 482 $sequences = array(); 483 $prefix = $DB->get_prefix(); 484 $rs = $DB->get_recordset_sql("SHOW TABLE STATUS LIKE ?", array($prefix.'%')); 485 foreach ($rs as $info) { 486 $table = strtolower($info->name); 487 if (strpos($table, $prefix) !== 0) { 488 // incorrect table match caused by _ 489 continue; 490 } 491 if (!is_null($info->auto_increment)) { 492 $table = preg_replace('/^'.preg_quote($prefix, '/').'/', '', $table); 493 $sequences[$table] = $info->auto_increment; 494 } 495 } 496 $rs->close(); 497 $prefix = $DB->get_prefix(); 498 foreach ($data as $table => $records) { 499 if (isset($structure[$table]['id']) and $structure[$table]['id']->auto_increment) { 500 if (isset($sequences[$table])) { 501 $nextid = self::get_next_sequence_starting_value($records); 502 if ($sequences[$table] != $nextid) { 503 $DB->change_database_structure("ALTER TABLE {$prefix}{$table} AUTO_INCREMENT = $nextid"); 504 } 505 506 } else { 507 // some problem exists, fallback to standard code 508 $DB->get_manager()->reset_sequence($table); 509 } 510 } 511 } 512 513 } else if ($dbfamily === 'oracle') { 514 $sequences = self::get_sequencenames(); 515 $sequences = array_map('strtoupper', $sequences); 516 $lookup = array_flip($sequences); 517 518 $current = array(); 519 list($seqs, $params) = $DB->get_in_or_equal($sequences); 520 $sql = "SELECT sequence_name, last_number FROM user_sequences WHERE sequence_name $seqs"; 521 $rs = $DB->get_recordset_sql($sql, $params); 522 foreach ($rs as $seq) { 523 $table = $lookup[$seq->sequence_name]; 524 $current[$table] = $seq->last_number; 525 } 526 $rs->close(); 527 528 foreach ($data as $table => $records) { 529 if (isset($structure[$table]['id']) and $structure[$table]['id']->auto_increment) { 530 $lastrecord = end($records); 531 if ($lastrecord) { 532 $nextid = $lastrecord->id + 1; 533 } else { 534 $nextid = 1; 535 } 536 if (!isset($current[$table])) { 537 $DB->get_manager()->reset_sequence($table); 538 } else if ($nextid == $current[$table]) { 539 continue; 540 } 541 // reset as fast as possible - alternatively we could use http://stackoverflow.com/questions/51470/how-do-i-reset-a-sequence-in-oracle 542 $seqname = $sequences[$table]; 543 $cachesize = $DB->get_manager()->generator->sequence_cache_size; 544 $DB->change_database_structure("DROP SEQUENCE $seqname"); 545 $DB->change_database_structure("CREATE SEQUENCE $seqname START WITH $nextid INCREMENT BY 1 NOMAXVALUE CACHE $cachesize"); 546 } 547 } 548 549 } else { 550 // note: does mssql support any kind of faster reset? 551 // This also implies mssql will not use unique sequence values. 552 if (is_null($empties)) { 553 $empties = self::guess_unmodified_empty_tables(); 554 } 555 foreach ($data as $table => $records) { 556 if (isset($empties[$table])) { 557 continue; 558 } 559 if (isset($structure[$table]['id']) and $structure[$table]['id']->auto_increment) { 560 $DB->get_manager()->reset_sequence($table); 561 } 562 } 563 } 564 } 565 566 /** 567 * Reset all database tables to default values. 568 * @static 569 * @return bool true if reset done, false if skipped 570 */ 571 public static function reset_database() { 572 global $DB; 573 574 $tables = $DB->get_tables(false); 575 if (!$tables or empty($tables['config'])) { 576 // not installed yet 577 return false; 578 } 579 580 if (!$data = self::get_tabledata()) { 581 // not initialised yet 582 return false; 583 } 584 if (!$structure = self::get_tablestructure()) { 585 // not initialised yet 586 return false; 587 } 588 589 $empties = self::guess_unmodified_empty_tables(); 590 591 $borkedmysql = false; 592 if ($DB->get_dbfamily() === 'mysql') { 593 $version = $DB->get_server_info(); 594 if (version_compare($version['version'], '5.6.0') == 1 and version_compare($version['version'], '5.6.16') == -1) { 595 // Everything that comes from Oracle is evil! 596 // 597 // See http://dev.mysql.com/doc/refman/5.6/en/alter-table.html 598 // You cannot reset the counter to a value less than or equal to to the value that is currently in use. 599 // 600 // From 5.6.16 release notes: 601 // InnoDB: The ALTER TABLE INPLACE algorithm would fail to decrease the auto-increment value. 602 // (Bug #17250787, Bug #69882) 603 $borkedmysql = true; 604 605 } else if (version_compare($version['version'], '10.0.0') == 1) { 606 // And MariaDB is no better! 607 // Let's hope they pick the patch sometime later... 608 $borkedmysql = true; 609 } 610 } 611 612 if ($borkedmysql) { 613 $mysqlsequences = array(); 614 $prefix = $DB->get_prefix(); 615 $rs = $DB->get_recordset_sql("SHOW TABLE STATUS LIKE ?", array($prefix.'%')); 616 foreach ($rs as $info) { 617 $table = strtolower($info->name); 618 if (strpos($table, $prefix) !== 0) { 619 // Incorrect table match caused by _ char. 620 continue; 621 } 622 if (!is_null($info->auto_increment)) { 623 $table = preg_replace('/^'.preg_quote($prefix, '/').'/', '', $table); 624 $mysqlsequences[$table] = $info->auto_increment; 625 } 626 } 627 } 628 629 foreach ($data as $table => $records) { 630 if ($borkedmysql) { 631 if (empty($records) and isset($empties[$table])) { 632 continue; 633 } 634 635 if (isset($structure[$table]['id']) and $structure[$table]['id']->auto_increment) { 636 $current = $DB->get_records($table, array(), 'id ASC'); 637 if ($current == $records) { 638 if (isset($mysqlsequences[$table]) and $mysqlsequences[$table] == $structure[$table]['id']->auto_increment) { 639 continue; 640 } 641 } 642 } 643 644 // Use TRUNCATE as a workaround and reinsert everything. 645 $DB->delete_records($table, null); 646 foreach ($records as $record) { 647 $DB->import_record($table, $record, false, true); 648 } 649 continue; 650 } 651 652 if (empty($records)) { 653 if (isset($empties[$table])) { 654 // table was not modified and is empty 655 } else { 656 $DB->delete_records($table, array()); 657 } 658 continue; 659 } 660 661 if (isset($structure[$table]['id']) and $structure[$table]['id']->auto_increment) { 662 $currentrecords = $DB->get_records($table, array(), 'id ASC'); 663 $changed = false; 664 foreach ($records as $id => $record) { 665 if (!isset($currentrecords[$id])) { 666 $changed = true; 667 break; 668 } 669 if ((array)$record != (array)$currentrecords[$id]) { 670 $changed = true; 671 break; 672 } 673 unset($currentrecords[$id]); 674 } 675 if (!$changed) { 676 if ($currentrecords) { 677 $lastrecord = end($records); 678 $DB->delete_records_select($table, "id > ?", array($lastrecord->id)); 679 continue; 680 } else { 681 continue; 682 } 683 } 684 } 685 686 $DB->delete_records($table, array()); 687 foreach ($records as $record) { 688 $DB->import_record($table, $record, false, true); 689 } 690 } 691 692 // reset all next record ids - aka sequences 693 self::reset_all_database_sequences($empties); 694 695 // remove extra tables 696 foreach ($tables as $table) { 697 if (!isset($data[$table])) { 698 $DB->get_manager()->drop_table(new xmldb_table($table)); 699 } 700 } 701 702 return true; 703 } 704 705 /** 706 * Purge dataroot directory 707 * @static 708 * @return void 709 */ 710 public static function reset_dataroot() { 711 global $CFG; 712 713 $childclassname = self::get_framework() . '_util'; 714 715 // Do not delete automatically installed files. 716 self::skip_original_data_files($childclassname); 717 718 // Clean up the dataroot folder. 719 $handle = opendir(self::get_dataroot()); 720 while (false !== ($item = readdir($handle))) { 721 if (in_array($item, $childclassname::$datarootskiponreset)) { 722 continue; 723 } 724 if (is_dir(self::get_dataroot()."/$item")) { 725 remove_dir(self::get_dataroot()."/$item", false); 726 } else { 727 unlink(self::get_dataroot()."/$item"); 728 } 729 } 730 closedir($handle); 731 732 // Clean up the dataroot/filedir folder. 733 if (file_exists(self::get_dataroot() . '/filedir')) { 734 $handle = opendir(self::get_dataroot() . '/filedir'); 735 while (false !== ($item = readdir($handle))) { 736 if (in_array('filedir/' . $item, $childclassname::$datarootskiponreset)) { 737 continue; 738 } 739 if (is_dir(self::get_dataroot()."/filedir/$item")) { 740 remove_dir(self::get_dataroot()."/filedir/$item", false); 741 } else { 742 unlink(self::get_dataroot()."/filedir/$item"); 743 } 744 } 745 closedir($handle); 746 } 747 748 make_temp_directory(''); 749 make_cache_directory(''); 750 make_localcache_directory(''); 751 // Reset the cache API so that it recreates it's required directories as well. 752 cache_factory::reset(); 753 // Purge all data from the caches. This is required for consistency. 754 // Any file caches that happened to be within the data root will have already been clearer (because we just deleted cache) 755 // and now we will purge any other caches as well. 756 cache_helper::purge_all(); 757 } 758 759 /** 760 * Gets a text-based site version description. 761 * 762 * @return string The site info 763 */ 764 public static function get_site_info() { 765 global $CFG; 766 767 $output = ''; 768 769 // All developers have to understand English, do not localise! 770 771 $release = null; 772 require("$CFG->dirroot/version.php"); 773 774 $output .= "Moodle $release, $CFG->dbtype"; 775 if ($hash = self::get_git_hash()) { 776 $output .= ", $hash"; 777 } 778 $output .= "\n"; 779 780 return $output; 781 } 782 783 /** 784 * Try to get current git hash of the Moodle in $CFG->dirroot. 785 * @return string null if unknown, sha1 hash if known 786 */ 787 public static function get_git_hash() { 788 global $CFG; 789 790 // This is a bit naive, but it should mostly work for all platforms. 791 792 if (!file_exists("$CFG->dirroot/.git/HEAD")) { 793 return null; 794 } 795 796 $headcontent = file_get_contents("$CFG->dirroot/.git/HEAD"); 797 if ($headcontent === false) { 798 return null; 799 } 800 801 $headcontent = trim($headcontent); 802 803 // If it is pointing to a hash we return it directly. 804 if (strlen($headcontent) === 40) { 805 return $headcontent; 806 } 807 808 if (strpos($headcontent, 'ref: ') !== 0) { 809 return null; 810 } 811 812 $ref = substr($headcontent, 5); 813 814 if (!file_exists("$CFG->dirroot/.git/$ref")) { 815 return null; 816 } 817 818 $hash = file_get_contents("$CFG->dirroot/.git/$ref"); 819 820 if ($hash === false) { 821 return null; 822 } 823 824 $hash = trim($hash); 825 826 if (strlen($hash) != 40) { 827 return null; 828 } 829 830 return $hash; 831 } 832 833 /** 834 * Drop the whole test database 835 * @static 836 * @param bool $displayprogress 837 */ 838 protected static function drop_database($displayprogress = false) { 839 global $DB; 840 841 $tables = $DB->get_tables(false); 842 if (isset($tables['config'])) { 843 // config always last to prevent problems with interrupted drops! 844 unset($tables['config']); 845 $tables['config'] = 'config'; 846 } 847 848 if ($displayprogress) { 849 echo "Dropping tables:\n"; 850 } 851 $dotsonline = 0; 852 foreach ($tables as $tablename) { 853 $table = new xmldb_table($tablename); 854 $DB->get_manager()->drop_table($table); 855 856 if ($dotsonline == 60) { 857 if ($displayprogress) { 858 echo "\n"; 859 } 860 $dotsonline = 0; 861 } 862 if ($displayprogress) { 863 echo '.'; 864 } 865 $dotsonline += 1; 866 } 867 if ($displayprogress) { 868 echo "\n"; 869 } 870 } 871 872 /** 873 * Drops the test framework dataroot 874 * @static 875 */ 876 protected static function drop_dataroot() { 877 global $CFG; 878 879 $framework = self::get_framework(); 880 $childclassname = $framework . '_util'; 881 882 $files = scandir(self::get_dataroot() . '/' . $framework); 883 foreach ($files as $file) { 884 if (in_array($file, $childclassname::$datarootskipondrop)) { 885 continue; 886 } 887 $path = self::get_dataroot() . '/' . $framework . '/' . $file; 888 if (is_dir($path)) { 889 remove_dir($path, false); 890 } else { 891 unlink($path); 892 } 893 } 894 895 $jsonfilepath = self::get_dataroot() . '/' . self::$originaldatafilesjson; 896 if (file_exists($jsonfilepath)) { 897 // Delete the json file. 898 unlink($jsonfilepath); 899 // Delete the dataroot filedir. 900 remove_dir(self::get_dataroot() . '/filedir', false); 901 } 902 } 903 904 /** 905 * Skip the original dataroot files to not been reset. 906 * 907 * @static 908 * @param string $utilclassname the util class name.. 909 */ 910 protected static function skip_original_data_files($utilclassname) { 911 $jsonfilepath = self::get_dataroot() . '/' . self::$originaldatafilesjson; 912 if (file_exists($jsonfilepath)) { 913 914 $listfiles = file_get_contents($jsonfilepath); 915 916 // Mark each files as to not be reset. 917 if (!empty($listfiles) && !self::$originaldatafilesjsonadded) { 918 $originaldatarootfiles = json_decode($listfiles); 919 // Keep the json file. Only drop_dataroot() should delete it. 920 $originaldatarootfiles[] = self::$originaldatafilesjson; 921 $utilclassname::$datarootskiponreset = array_merge($utilclassname::$datarootskiponreset, 922 $originaldatarootfiles); 923 self::$originaldatafilesjsonadded = true; 924 } 925 } 926 } 927 928 /** 929 * Save the list of the original dataroot files into a json file. 930 */ 931 protected static function save_original_data_files() { 932 global $CFG; 933 934 $jsonfilepath = self::get_dataroot() . '/' . self::$originaldatafilesjson; 935 936 // Save the original dataroot files if not done (only executed the first time). 937 if (!file_exists($jsonfilepath)) { 938 939 $listfiles = array(); 940 $listfiles['filedir/.'] = 'filedir/.'; 941 $listfiles['filedir/..'] = 'filedir/..'; 942 943 $filedir = self::get_dataroot() . '/filedir'; 944 if (file_exists($filedir)) { 945 $directory = new RecursiveDirectoryIterator($filedir); 946 foreach (new RecursiveIteratorIterator($directory) as $file) { 947 if ($file->isDir()) { 948 $key = substr($file->getPath(), strlen(self::get_dataroot() . '/')); 949 } else { 950 $key = substr($file->getPathName(), strlen(self::get_dataroot() . '/')); 951 } 952 $listfiles[$key] = $key; 953 } 954 } 955 956 // Save the file list in a JSON file. 957 $fp = fopen($jsonfilepath, 'w'); 958 fwrite($fp, json_encode(array_values($listfiles))); 959 fclose($fp); 960 } 961 } 962 }
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 |