Merge branch 'w12_MDL-31857_m23_phpunit' of git://github.com/skodak/moodle

This commit is contained in:
Sam Hemelryk
2012-03-22 10:44:04 +13:00
36 changed files with 1896 additions and 29 deletions
+1
View File
@@ -25,3 +25,4 @@ CVS
/.project
/.buildpath
/.cache
/phpunit.xml
+93
View File
@@ -0,0 +1,93 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* PHPUnit related utilities.
*
* Exit codes:
* 0 - success
* 1 - general error
* 130 - coding error
* 131 - configuration problem
* 133 - drop existing data before installing
*
* @package tool_phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('PHPUNIT_CLI_UTIL', true);
require(__DIR__ . '/../../../../lib/phpunit/bootstrap.php');
require_once($CFG->libdir.'/phpunit/lib.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/upgradelib.php');
require_once($CFG->libdir.'/clilib.php');
require_once($CFG->libdir.'/pluginlib.php');
require_once($CFG->libdir.'/installlib.php');
// now get cli options
list($options, $unrecognized) = cli_get_params(
array(
'drop' => false,
'install' => false,
'buildconfig' => false,
'help' => false,
),
array(
'h' => 'help'
)
);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
$drop = $options['drop'];
$install = $options['install'];
$buildconfig = $options['buildconfig'];
if ($options['help'] or (!$drop and !$install and !$buildconfig)) {
$help = "Various PHPUnit utility functions
Options:
--drop Drop database and dataroot
--install Install database
--buildconfig Build /phpunit.xml from /phpunit.xml.dist that includes suites for all plugins and core
-h, --help Print out this help
Example:
\$/usr/bin/php lib/phpunit/tool.php
";
echo $help;
die;
}
if ($buildconfig) {
phpunit_util::build_config_file();
exit(0);
} else if ($drop) {
phpunit_util::drop_site();
// note: we must stop here because $CFG is messed up and we can not reinstall, sorry
exit(0);
} else if ($install) {
phpunit_util::install_site();
exit(0);
}
+39
View File
@@ -0,0 +1,39 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* PHPUnit info
*
* @package tool_phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
*/
define('NO_OUTPUT_BUFFERING', true);
require(dirname(__FILE__) . '/../../../config.php');
require_once($CFG->libdir.'/adminlib.php');
admin_externalpage_setup('toolphpunit');
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('pluginname', 'tool_phpunit'));
echo $OUTPUT->box_start();
$info = file_get_contents("$CFG->libdir/phpunit/readme.md");
echo markdown_to_html($info);
echo $OUTPUT->box_end();
echo $OUTPUT->footer();
@@ -0,0 +1,25 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'tool_phpunit'
*
* @package tool_phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'PHPUnit tests';
+28
View File
@@ -0,0 +1,28 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* PHPunit integration
*
* @package tool_phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
$ADMIN->add('development', new admin_externalpage('toolphpunit', get_string('pluginname', 'tool_phpunit'), "$CFG->wwwroot/$CFG->admin/tool/phpunit/index.php"));
+30
View File
@@ -0,0 +1,30 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Plugin version info
*
* @package tool_phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2012030800; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2012030100; // Requires this Moodle version
$plugin->component = 'tool_phpunit'; // Full name of the plugin (used for diagnostics)
+7 -1
View File
@@ -479,7 +479,13 @@ $CFG->admin = 'admin';
// Example:
// $CFG->forced_plugin_settings = array('pluginname' => array('settingname' => 'value', 'secondsetting' => 'othervalue'),
// 'otherplugin' => array('mysetting' => 'myvalue', 'thesetting' => 'thevalue'));
//
//=========================================================================
// 9. PHPUNIT SUPPORT
//=========================================================================
// $CFG->phpunit_prefix = 'phpu_';
// $CFG->phpunit_dataroot = '/home/example/phpu_moodledata';
// $CFG->phpunit_directorypermissions = 02777; // optional
//=========================================================================
// ALL DONE! To continue installation, visit your main page with a browser
+1 -1
View File
@@ -217,7 +217,7 @@ $ACCESSLIB_PRIVATE->capabilities = null; // detailed information about th
*/
function accesslib_clear_all_caches_for_unit_testing() {
global $UNITTEST, $USER;
if (empty($UNITTEST->running)) {
if (empty($UNITTEST->running) and !PHPUNITTEST) {
throw new coding_exception('You must not call clear_all_caches outside of unit tests.');
}
+3 -6
View File
@@ -112,18 +112,15 @@ class database_manager {
/**
* Reset a sequence to the id field of a table.
* @param string $table Name of table.
* @throws ddl_exception|ddl_table_missing_exception Exception thrown upon reset errors.
* @param string|xmldb_table $table Name of table.
* @throws ddl_exception thrown upon reset errors.
*/
public function reset_sequence($table) {
if (!is_string($table) and !($table instanceof xmldb_table)) {
throw new ddl_exception('ddlunknownerror', NULL, 'incorrect table parameter!');
}
/// Check the table exists
if (!$this->table_exists($table)) {
throw new ddl_table_missing_exception($table);
}
// Do not test if table exists because it is slow
if (!$sqlarr = $this->generator->getResetSequenceSQL($table)) {
throw new ddl_exception('ddlunknownerror', null, 'table reset sequence sql not generated');
+2
View File
@@ -8,6 +8,8 @@ if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
global $CFG;
require_once($CFG->libdir . '/adminlib.php');
class ddl_test extends UnitTestCase {
+1 -1
View File
@@ -7775,7 +7775,7 @@ function get_plugin_types($fullpaths=true) {
function get_plugin_list($plugintype) {
global $CFG;
$ignored = array('CVS', '_vti_cnf', 'simpletest', 'db', 'yui', 'phpunit');
$ignored = array('CVS', '_vti_cnf', 'simpletest', 'db', 'yui', 'tests');
if ($plugintype == 'auth') {
// Historically we have had an auth plugin called 'db', so allow a special case.
$key = array_search('db', $ignored);
+208
View File
@@ -0,0 +1,208 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Prepares PHPUnit environment, it is called automatically.
*
* Exit codes:
* 0 - success
* 1 - general error
* 130 - coding error
* 131 - configuration problem
* 132 - drop data, then install new test database
*
* @package core_core
* @category phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// we want to know about all problems
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', '1');
ini_set('log_errors', '1');
if (isset($_SERVER['REMOTE_ADDR'])) {
phpunit_bootstrap_error('Unit tests can be executed only from commandline!', 1);
}
if (defined('PHPUNITTEST')) {
phpunit_bootstrap_error("PHPUNITTEST constant must not be manually defined anywhere!", 130);
}
define('PHPUNITTEST', true);
if (defined('CLI_SCRIPT')) {
phpunit_bootstrap_error('CLI_SCRIPT must not be manually defined in any PHPUnit test scripts', 130);
}
define('CLI_SCRIPT', true);
define('NO_OUTPUT_BUFFERING', true);
// only load CFG from config.php
define('ABORT_AFTER_CONFIG', true);
require(__DIR__ . '/../../config.php');
// remove error handling overrides done in config.php
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('log_errors', '1');
// prepare dataroot
umask(0);
if (isset($CFG->phpunit_directorypermissions)) {
$CFG->directorypermissions = $CFG->phpunit_directorypermissions;
} else {
$CFG->directorypermissions = 02777;
}
$CFG->filepermissions = ($CFG->directorypermissions & 0666);
if (!isset($CFG->phpunit_dataroot)) {
phpunit_bootstrap_error('Missing $CFG->phpunit_dataroot in config.php, can not run tests!', 131);
}
if (isset($CFG->dataroot) and $CFG->phpunit_dataroot === $CFG->dataroot) {
phpunit_bootstrap_error('$CFG->dataroot and $CFG->phpunit_dataroot must not be identical, can not run tests!', 131);
}
if (!file_exists($CFG->phpunit_dataroot)) {
mkdir($CFG->phpunit_dataroot, $CFG->directorypermissions);
}
if (!is_dir($CFG->phpunit_dataroot)) {
phpunit_bootstrap_error('$CFG->phpunit_dataroot directory can not be created, can not run tests!', 131);
}
if (!is_writable($CFG->phpunit_dataroot)) {
// try to fix premissions if possible
if (function_exists('posix_getuid')) {
$chmod = fileperms($CFG->phpunit_dataroot);
if (fileowner($dir) == posix_getuid()) {
$chmod = $chmod | 0700;
chmod($CFG->phpunit_dataroot, $chmod);
}
}
if (!is_writable($CFG->phpunit_dataroot)) {
phpunit_bootstrap_error('$CFG->phpunit_dataroot directory is not writable, can not run tests!', 131);
}
}
if (!file_exists("$CFG->phpunit_dataroot/phpunittestdir.txt")) {
if ($dh = opendir($CFG->phpunit_dataroot)) {
while (($file = readdir($dh)) !== false) {
if ($file === 'phpunit' or $file === '.' or $file === '..' or $file === '.DS_store') {
continue;
}
phpunit_bootstrap_error('$CFG->phpunit_dataroot directory is not empty, can not run tests! Is it used for anything else?', 131);
}
closedir($dh);
unset($dh);
unset($file);
}
// now we are 100% sure this dir is used only for phpunit tests
phpunit_bootstrap_initdataroot($CFG->phpunit_dataroot);
}
// verify db prefix
if (!isset($CFG->phpunit_prefix)) {
phpunit_bootstrap_error('Missing $CFG->phpunit_dataroot in config.php, can not run tests!', 131);
}
if (isset($CFG->prefix) and $CFG->prefix === $CFG->phpunit_prefix) {
phpunit_bootstrap_error('$CFG->prefix and $CFG->phpunit_prefix must not be identical, can not run tests!', 131);
}
// throw away standard CFG settings
$CFG->dataroot = $CFG->phpunit_dataroot;
$CFG->prefix = $CFG->phpunit_prefix;
$allowed = array('wwwroot', 'dataroot', 'dirroot', 'admin', 'directorypermissions', 'filepermissions',
'dbtype', 'dblibrary', 'dbhost', 'dbname', 'dbuser', 'dbpass', 'prefix', 'dboptions');
$productioncfg = (array)$CFG;
$CFG = new stdClass();
foreach ($productioncfg as $key=>$value) {
if (!in_array($key, $allowed) and strpos($key, 'phpunit_') !== 0) {
// ignore
continue;
}
$CFG->{$key} = $value;
}
unset($key);
unset($value);
unset($allowed);
unset($productioncfg);
// force the same CFG settings in all sites
$CFG->debug = (E_ALL | E_STRICT | 38911); // can not use DEBUG_DEVELOPER here
$CFG->debugdisplay = 1;
error_reporting($CFG->debug);
ini_set('display_errors', '1');
ini_set('log_errors', '0');
$CFG->noemailever = true; // better not mail anybody from tests, override temporarily if necessary
$CFG->cachetext = 0; // disable this very nasty setting
// some ugly hacks
$CFG->themerev = 1;
$CFG->jsrev = 1;
// load test case stub classes and other stuff
require_once("$CFG->dirroot/lib/phpunit/lib.php");
// finish moodle init
define('ABORT_AFTER_CONFIG_CANCEL', true);
require("$CFG->dirroot/lib/setup.php");
raise_memory_limit(MEMORY_EXTRA);
if (defined('PHPUNIT_CLI_UTIL')) {
// all other tests are done in the CLI scripts...
return;
}
if (!phpunit_util::is_testing_ready()) {
phpunit_bootstrap_error('Database is not initialised to run unit tests, please use "php admin/tool/phpunit/cli/util.php --install"', 132);
}
// refresh data in all tables, clear caches, etc.
phpunit_util::reset_all_data();
// store fresh globals
phpunit_util::init_globals();
//=========================================================
/**
* Print error and stop execution
* @param $text
* @param int $errorcode
* @return void - stops code execution with error code
*/
function phpunit_bootstrap_error($text, $errorcode = 1) {
fwrite(STDERR, $text."\n");
exit($errorcode);
}
/**
* Mark empty dataroot to be used for testing.
* @param $dataroot
* @return void
*/
function phpunit_bootstrap_initdataroot($dataroot) {
global $CFG;
file_put_contents("$dataroot/phpunittestdir.txt", 'Contents of this directory are used during tests only, do not delete this file!');
chmod("$dataroot/phpunittestdir.txt", $CFG->filepermissions);
if (!file_exists("$CFG->phpunit_dataroot/phpunit")) {
mkdir("$CFG->phpunit_dataroot/phpunit", $CFG->directorypermissions);
}
}
+602
View File
@@ -0,0 +1,602 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Various PHPUnit classes and functions
*
* @package core_core
* @category phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once 'PHPUnit/Autoload.php'; // necessary when loaded from cli/util.php script
/**
* Collection of utility methods.
*
* @package core_phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class phpunit_util {
/**
* @var array original content of all database tables
*/
protected static $tabledata = null;
protected static $globals = array();
/**
* Returns contents of all tables right after installation.
* @static
* @return array $table=>$records
*/
protected static function get_tabledata() {
global $CFG;
if (!isset(self::$tabledata)) {
$data = file_get_contents("$CFG->dataroot/phpunit/tabledata.ser");
self::$tabledata = unserialize($data);
}
if (!is_array(self::$tabledata)) {
phpunit_bootstrap_error('Can not read dataroot/phpunit/tabledata.ser or invalid format!');
}
return self::$tabledata;
}
/**
* Initialise CFG using data from fresh new install.
* @static
*/
public static function initialise_cfg() {
global $CFG, $DB;
if (!file_exists("$CFG->dataroot/phpunit/tabledata.ser")) {
// most probably PHPUnit CLI installer
return;
}
if (!$DB->get_manager()->table_exists('config') or !$DB->count_records('config')) {
@unlink("$CFG->dataroot/phpunit/tabledata.ser");
@unlink("$CFG->dataroot/phpunit/versionshash.txt");
self::$tabledata = null;
return;
}
$data = self::get_tabledata();
foreach($data['config'] as $record) {
$name = $record->name;
$value = $record->value;
if (property_exists($CFG, $name)) {
// config.php settings always take precedence
continue;
}
$CFG->{$name} = $value;
}
}
/**
* Reset contents of all database tables to initial values, reset caches, etc.
*
* Note: this is relatively slow (cca 2 seconds for pg and 7 for mysql) - please use with care!
*
* @static
*/
public static function reset_all_data() {
global $DB, $CFG;
$data = self::get_tabledata();
$trans = $DB->start_delegated_transaction(); // faster and safer
foreach ($data as $table=>$records) {
$DB->delete_records($table, array());
$resetseq = null;
foreach ($records as $record) {
if (is_null($resetseq)) {
$resetseq = property_exists($record, 'id');
}
$DB->import_record($table, $record, false, true);
}
if ($resetseq === true) {
$DB->get_manager()->reset_sequence($table, true);
}
}
$trans->allow_commit();
purge_all_caches();
$user = new stdClass();
$user->id = 0;
$user->mnet = 0;
$user->mnethostid = $CFG->mnet_localhost_id;
session_set_user($user);
accesslib_clear_all_caches_for_unit_testing();
}
/**
* Called during bootstrap only!
* @static
* @return void
*/
public static function init_globals() {
global $CFG;
self::$globals['CFG'] = clone($CFG);
}
/**
* Returns original state of global variable.
* @static
* @param string $name
* @return mixed
*/
public static function get_global_backup($name) {
if (isset(self::$globals[$name])) {
if (is_object(self::$globals[$name])) {
$return = clone(self::$globals[$name]);
return $return;
} else {
return self::$globals[$name];
}
}
return null;
}
/**
* Does this site (db and dataroot) appear to be used for production?
* We try very hard to prevent accidental damage done to production servers!!
*
* @static
* @return bool
*/
public static function is_test_site() {
global $DB, $CFG;
if (!file_exists("$CFG->dataroot/phpunittestdir.txt")) {
// this is already tested in bootstrap script,
// but anway presence of this file means the dataroot is for testing
return false;
}
$tables = $DB->get_tables(false);
if ($tables) {
if (!$DB->get_manager()->table_exists('config')) {
return false;
}
if (!get_config('core', 'phpunittest')) {
return false;
}
}
return true;
}
/**
* Is this site initialised to run unit tests?
*
* @static
* @return bool
*/
public static function is_testing_ready() {
global $DB, $CFG;
if (!self::is_test_site()) {
return false;
}
$tables = $DB->get_tables(true);
if (!$tables) {
return false;
}
if (!get_config('core', 'phpunittest')) {
return false;
}
if (!file_exists("$CFG->dataroot/phpunit/tabledata.ser")) {
return false;
}
if (!file_exists("$CFG->dataroot/phpunit/versionshash.txt")) {
return false;
}
$hash = phpunit_util::get_version_hash();
$oldhash = file_get_contents("$CFG->dataroot/phpunit/versionshash.txt");
if ($hash !== $oldhash) {
return false;
}
return true;
}
/**
* Drop all test site data.
*
* Note: To be used from CLI scripts only.
*
* @static
* @return void, may terminate execution with exit code
*/
public static function drop_site() {
global $DB, $CFG;
if (!self::is_test_site()) {
cli_error('Can not drop non-test sites!!', 131);
}
// drop dataroot
remove_dir($CFG->dataroot, true);
phpunit_bootstrap_initdataroot($CFG->dataroot);
// drop all tables
$trans = $DB->start_delegated_transaction();
$tables = $DB->get_tables(false);
foreach ($tables as $tablename) {
$DB->delete_records($tablename, array());
}
$trans->allow_commit();
// now drop them
foreach ($tables as $tablename) {
$table = new xmldb_table($tablename);
$DB->get_manager()->drop_table($table);
}
}
/**
* Perform a fresh test site installation
*
* Note: To be used from CLI scripts only.
*
* @static
* @return void, may terminate execution with exit code
*/
public static function install_site() {
global $DB, $CFG;
if (!self::is_test_site()) {
cli_error('Can not install non-test sites!!', 131);
}
if ($DB->get_tables()) {
cli_error('Database tables already installed, drop the site first.', 133);
}
$options = array();
$options['adminpass'] = 'admin'; // removed later
$options['shortname'] = 'phpunit';
$options['fullname'] = 'PHPUnit test site';
install_cli_database($options, false);
// just in case remove admin password so that normal login is not possible
$DB->set_field('user', 'password', 'not cached', array('username' => 'admin'));
// add test db flag
set_config('phpunittest', 'phpunittest');
// store data for all tables
$data = array();
$tables = $DB->get_tables();
foreach ($tables as $table) {
$data[$table] = $DB->get_records($table, array());
}
$data = serialize($data);
@unlink("$CFG->dataroot/phpunit/tabledata.ser");
file_put_contents("$CFG->dataroot/phpunit/tabledata.ser", $data);
// hash all plugin versions - helps with very fast detection of db structure changes
$hash = phpunit_util::get_version_hash();
@unlink("$CFG->dataroot/phpunit/versionshash.txt");
file_put_contents("$CFG->dataroot/phpunit/versionshash.txt", $hash);
}
/**
* Culculate unique version hash for all available plugins and core.
* @static
* @return string sha1 hash
*/
public static function get_version_hash() {
global $CFG;
$versions = array();
// main version first
$version = null;
include($CFG->dirroot.'/version.php');
$versions['core'] = $version;
// modules
$mods = get_plugin_list('mod');
ksort($mods);
foreach ($mods as $mod => $fullmod) {
$module = new stdClass();
$module->version = null;
include($fullmod.'/version.php');
$versions[$mod] = $module->version;
}
// now the rest of plugins
$plugintypes = get_plugin_types();
unset($plugintypes['mod']);
ksort($plugintypes);
foreach ($plugintypes as $type=>$unused) {
$plugs = get_plugin_list($type);
ksort($plugs);
foreach ($plugs as $plug=>$fullplug) {
$plugin = new stdClass();
$plugin->version = null;
@include($fullplug.'/version.php');
$versions[$plug] = $plugin->version;
}
}
$hash = sha1(serialize($versions));
return $hash;
}
/**
* Builds /phpunit.xml file using defaults from /phpunit.xml.dist
* @static
* @return void
*/
public static function build_config_file() {
global $CFG;
$template = '
<testsuites>
<testsuite name="@component@">
<directory suffix="_test.php">@dir@</directory>
</testsuite>
</testsuites>';
$data = file_get_contents("$CFG->dirroot/phpunit.xml.dist");
$suites = '';
$plugintypes = get_plugin_types();
ksort($plugintypes);
foreach ($plugintypes as $type=>$unused) {
$plugs = get_plugin_list($type);
ksort($plugs);
foreach ($plugs as $plug=>$fullplug) {
if (!file_exists("$fullplug/tests/")) {
continue;
}
$dir = preg_replace("|$CFG->dirroot/|", '', $fullplug, 1);
$dir .= '/tests';
$component = $type.'_'.$plug;
$suite = str_replace('@component@', $component, $template);
$suite = str_replace('@dir@', $dir, $suite);
$suites .= $suite;
}
}
$data = preg_replace('|<!--@plugin_suits_start@-->.*<!--@plugin_suits_end@-->|s', $suites, $data, 1);
@unlink("$CFG->dirroot/phpunit.xml");
file_put_contents("$CFG->dirroot/phpunit.xml", $data);
}
}
/**
* Simplified emulation test case for legacy SimpleTest.
*
* Note: this is supposed to work for very simple tests only.
*
* @deprecated
* @package core_phpunit
* @author Petr Skoda
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class UnitTestCase extends PHPUnit_Framework_TestCase {
/**
* @deprecated
* @param bool $expected
* @param string $message
* @return void
*/
public function expectException($expected, $message = '') {
// use phpdocs: @expectedException ExceptionClassName
if (!$expected) {
return;
}
$this->setExpectedException('moodle_exception', $message);
}
/**
* @deprecated
* @param bool $expected
* @param string $message
* @return void
*/
public static function expectError($expected = false, $message = '') {
// not available in PHPUnit
if (!$expected) {
return;
}
self::skipIf(true);
}
/**
* @deprecated
* @static
* @param mixed $actual
* @param string $messages
* @return void
*/
public static function assertTrue($actual, $messages = '') {
parent::assertTrue((bool)$actual, $messages);
}
/**
* @deprecated
* @static
* @param mixed $actual
* @param string $messages
* @return void
*/
public static function assertFalse($actual, $messages = '') {
parent::assertFalse((bool)$actual, $messages);
}
/**
* @deprecated
* @static
* @param mixed $expected
* @param mixed $actual
* @param string $message
* @return void
*/
public static function assertEqual($expected, $actual, $message = '') {
parent::assertEquals($expected, $actual, $message);
}
/**
* @deprecated
* @static
* @param mixed $expected
* @param mixed $actual
* @param string $message
* @return void
*/
public static function assertNotEqual($expected, $actual, $message = '') {
parent::assertNotEquals($expected, $actual, $message);
}
/**
* @deprecated
* @static
* @param mixed $expected
* @param mixed $actual
* @param string $message
* @return void
*/
public static function assertIdentical($expected, $actual, $message = '') {
parent::assertSame($expected, $actual, $message);
}
/**
* @deprecated
* @static
* @param mixed $expected
* @param mixed $actual
* @param string $message
* @return void
*/
public static function assertNotIdentical($expected, $actual, $message = '') {
parent::assertNotSame($expected, $actual, $message);
}
/**
* @deprecated
* @static
* @param mixed $actual
* @param mixed $expected
* @param string $message
* @return void
*/
public static function assertIsA($actual, $expected, $message = '') {
parent::assertInstanceOf($expected, $actual, $message);
}
}
/**
* The simplest PHPUnit test case customised for Moodle,
* do not modify database or any globals.
*
* @package core_phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class basic_testcase extends PHPUnit_Framework_TestCase {
/**
* Constructs a test case with the given name.
*
* @param string $name
* @param array $data
* @param string $dataName
*/
public function __construct($name = NULL, array $data = array(), $dataName = '') {
parent::__construct($name, $data, $dataName);
$this->setBackupGlobals(false);
$this->setBackupStaticAttributes(false);
$this->setRunTestInSeparateProcess(false);
$this->setInIsolation(false);
}
/**
* Runs the bare test sequence.
* @return void
*/
public function runBare() {
global $CFG, $USER, $DB;
$dbwrites = $DB->perf_get_writes();
parent::runBare();
$oldcfg = phpunit_util::get_global_backup('CFG');
foreach($CFG as $k=>$v) {
if (!property_exists($oldcfg, $k)) {
unset($CFG->$k);
error_log('warning: unexpected new $CFG->'.$k.' value in testcase: '.get_class($this).'->'.$this->getName(true));
} else if ($oldcfg->$k !== $CFG->$k) {
$CFG->$k = $oldcfg->$k;
error_log('warning: unexpected change of $CFG->'.$k.' value in testcase: '.get_class($this).'->'.$this->getName(true));
}
unset($oldcfg->$k);
}
if ($oldcfg) {
foreach($oldcfg as $k=>$v) {
$CFG->$k = $v;
error_log('warning: unexpected removal of $CFG->'.$k.' in testcase: '.get_class($this).'->'.$this->getName(true));
}
}
if ($USER->id != 0) {
error_log('warning: unexpected change of $USER in testcase: '.get_class($this).'->'.$this->getName(true));
$USER = new stdClass();
$USER->id = 0;
}
if ($dbwrites != $DB->perf_get_writes()) {
//TODO: find out what was changed exactly
error_log('warning: unexpected database modification, resetting DB state in testcase: '.get_class($this).'->'.$this->getName(true));
phpunit_util::reset_all_data();
}
//TODO: somehow find out if there are changes in dataroot
}
}
+49
View File
@@ -0,0 +1,49 @@
PHPUnit testing support in Moodle
==================================
Installation
------------
1. install PHPUnit PEAR extension - see [PHPUnit docs](http://www.phpunit.de/manual/current/en/installation.html) for more details
2. edit main config.php - add $CFG->phpunit_prefix and $CFG->phpunit_dataroot - see config-dist.php for more details
3. execute `php admin/tool/phpunit/cli/util.php --install` to initialise test database
4. it is necessary to reinitialise the test database manually after every upgrade or installation of new plugins
Test execution
--------------
* optionally generate phpunit.xml by executing `php admin/tool/phpunit/cli/util.php --buildconfig` - it collects test cases from all plugins
* execute `phpunit` shell command from dirroot directory
* you can also execute a single test `phpunit core_phpunit_basic_testcase lib/tests/phpunit_test.php`
* or all tests in one directory `phpunit --configuration phpunit.xml lib/tests/*_test.php`
* it is possible to create custom configuration files in xml format and use `phpunit -c myconfig.xml`
How to add more tests
---------------------
1. create `tests` directory in any plugin
2. add `*_test.php` files with custom class that extends `basic_testcase`
3. manually add all core unit test locations to `phpunit.xml.dist`
How to convert existing tests
-----------------------------
1. create new test file in `xxx/tests/yyy_test.php`
2. copy contents of the old test file
3. replace `extends UnitTestCase` with `extends basic_testcase`
4. fix setUp, tearDown, asserts, etc.
5. some old SimpleTest tests can be executed directly - mocking, database operations, assert(), etc. does not work, you may need to add `global $CFG;` before includes
FAQs
----
* Why is it necessary to execute the tests from commandline? PHPUnit is designed to be executed from shell, existing Moodle globals and constants would interfere with it.
* Why `tests` subdirectory? It should not collide with any plugin name because plugin names use singular form.
* Why is it necessary to include core and plugin suites in configuration files? PHPUnit does not seem to allow dynamic loading of tests from our dir structure.
TODO
----
* stage 2 - implement advaced_testcase - support for database modifications, object generators, automatic rollback of db, blobals and dataroot
* stage 3 - mocking and other advanced features, add support for execution of functional DB tests for different engines together (new options in phpunit.xml)
* other - support for execution of tests and cli/util.php from web UI (to be implemented via shell execution), shell script that prepares everything for the first execution
+16 -13
View File
@@ -45,7 +45,7 @@
global $CFG; // this should be done much earlier in config.php before creating new $CFG instance
if (!isset($CFG)) {
if (defined('PHPUNIT_SCRIPT') and PHPUNIT_SCRIPT) {
if (defined('PHPUNITTEST') and PHPUNITTEST) {
echo('There is a missing "global $CFG;" at the beginning of the config.php file.'."\n");
exit(1);
} else {
@@ -112,7 +112,10 @@ if (!isset($CFG->cachedir)) {
// directory of the script when run from the command line. The require_once()
// would fail, so we'll have to chdir()
if (!isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['argv'][0])) {
chdir(dirname($_SERVER['argv'][0]));
// do it only once - skip the second time when continuing after prevous abort
if (!defined('ABORT_AFTER_CONFIG') and !defined('ABORT_AFTER_CONFIG_CANCEL')) {
chdir(dirname($_SERVER['argv'][0]));
}
}
// sometimes default PHP settings are borked on shared hosting servers, I wonder why they have to do that??
@@ -130,6 +133,11 @@ if (!defined('NO_OUTPUT_BUFFERING')) {
define('NO_OUTPUT_BUFFERING', false);
}
// PHPUnit tests need custom init
if (!defined('PHPUNITTEST')) {
define('PHPUNITTEST', false);
}
// Servers should define a default timezone in php.ini, but if they don't then make sure something is defined.
// This is a quick hack. Ideally we should ask the admin for a value. See MDL-22625 for more on this.
if (function_exists('date_default_timezone_set') and function_exists('date_default_timezone_get')) {
@@ -139,15 +147,6 @@ if (function_exists('date_default_timezone_set') and function_exists('date_defau
unset($olddebug);
}
// PHPUnit scripts are a special case, for now we treat them as normal CLI scripts,
// please note you must install PHPUnit library separately via PEAR
if (!defined('PHPUNIT_SCRIPT')) {
define('PHPUNIT_SCRIPT', false);
}
if (PHPUNIT_SCRIPT) {
define('CLI_SCRIPT', true);
}
// Detect CLI scripts - CLI scripts are executed from command line, do not have session and we do not want HTML in output
// In your new CLI scripts just add "define('CLI_SCRIPT', true);" before requiring config.php.
// Please note that one script can not be accessed from both CLI and web interface.
@@ -471,7 +470,11 @@ if (isset($CFG->debug)) {
}
// Load up any configuration from the config table
initialise_cfg();
if (PHPUNITTEST) {
phpunit_util::initialise_cfg();
} else {
initialise_cfg();
}
// Verify upgrade is not running unless we are in a script that needs to execute in any case
if (!defined('NO_UPGRADE_CHECK') and isset($CFG->upgraderunning)) {
@@ -820,7 +823,7 @@ if (!empty($CFG->customscripts)) {
}
}
if (CLI_SCRIPT and !defined('WEB_CRON_EMULATED_CLI') and !PHPUNIT_SCRIPT) {
if ((CLI_SCRIPT and !defined('WEB_CRON_EMULATED_CLI')) or PHPUNITTEST) {
// no ip blocking
} else if (!empty($CFG->allowbeforeblock)) { // allowed list processed before blocked list?
// in this case, ip in allowed list will be performed first
+3
View File
@@ -26,6 +26,9 @@
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
global $CFG;
require_once($CFG->libdir.'/componentlib.class.php');
class componentlib_test extends UnitTestCase {
+3
View File
@@ -26,6 +26,9 @@
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
global $CFG;
require_once($CFG->libdir . '/csslib.php');
+3
View File
@@ -27,6 +27,9 @@
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
global $CFG;
require_once($CFG->libdir . '/externallib.php');
class externallib_test extends UnitTestCase {
+3
View File
@@ -27,6 +27,9 @@
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
global $CFG;
require_once($CFG->libdir . '/filelib.php');
class filelib_test extends UnitTestCase {
+1
View File
@@ -27,6 +27,7 @@
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
require_once($CFG->libdir . '/formslib.php');
require_once($CFG->libdir . '/form/radio.php');
require_once($CFG->libdir . '/form/select.php');
+3
View File
@@ -27,6 +27,9 @@
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
global $CFG;
require_once($CFG->libdir . '/outputcomponents.php');
+6 -3
View File
@@ -26,6 +26,9 @@
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
global $CFG;
require_once($CFG->libdir . '/navigationlib.php');
class navigation_node_test extends UnitTestCase {
@@ -52,7 +55,7 @@ class navigation_node_test extends UnitTestCase {
$this->activeurl = $PAGE->url;
navigation_node::override_active_url($this->activeurl);
$this->inactiveurl = new moodle_url('http://www.moodle.com/');
$this->fakeproperties['action'] = $this->inactiveurl;
@@ -165,11 +168,11 @@ class navigation_node_test extends UnitTestCase {
public function test_find_active_node() {
$activenode1 = $this->node->find_active_node();
$activenode2 = $this->node->get('demo1')->find_active_node();
if ($this->assertIsA($activenode1, 'navigation_node')) {
$this->assertReference($activenode1, $this->node->get('demo3')->get('demo5')->get('activity1'));
}
$this->assertNotA($activenode2, 'navigation_node');
}
+4 -1
View File
@@ -24,6 +24,9 @@
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/outputcomponents.php');
/**
@@ -142,7 +145,7 @@ EOF;
$this->assertTrue($menu instanceof custom_menu);
$this->assertTrue($menu->has_children());
$firstlevel = $menu->get_children();
$this->assertIsA($firstlevel, 'array');
$this->assertTrue(is_array($firstlevel));
$this->assertEqual(2, count($firstlevel));
$item = array_shift($firstlevel);
+2
View File
@@ -26,6 +26,8 @@
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
+2
View File
@@ -14,6 +14,8 @@ if (!defined('MOODLE_INTERNAL')) {
* If networking/proxy configuration is wrong these tests will fail..
*/
global $CFG;
require_once($CFG->libdir.'/simplepie/moodle_simplepie.php');
class moodlesimplepie_test extends UnitTestCase {
+185
View File
@@ -0,0 +1,185 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for the HTMLPurifier integration
*
* @package core_core
* @category phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
class core_htmlpurifier_testcase extends basic_testcase {
/**
* Verify _blank target is allowed
* @return void
*/
public function test_allow_blank_target() {
$text = '<a href="http://moodle.org" target="_blank">Some link</a>';
$result = format_text($text, FORMAT_HTML);
$this->assertSame($text, $result);
$result = format_text('<a href="http://moodle.org" target="some">Some link</a>', FORMAT_HTML);
$this->assertSame('<a href="http://moodle.org">Some link</a>', $result);
}
/**
* Verify our nolink tag accepted
* @return void
*/
public function test_nolink() {
// we can not use format text because nolink changes result
$text = '<nolink><div>no filters</div></nolink>';
$result = purify_html($text, array());
$this->assertSame($text, $result);
$text = '<nolink>xxx<em>xx</em><div>xxx</div></nolink>';
$result = purify_html($text, array());
$this->assertSame($text, $result);
}
/**
* Verify our tex tag accepted
* @return void
*/
public function test_tex() {
$text = '<tex>a+b=c</tex>';
$result = purify_html($text, array());
$this->assertSame($text, $result);
}
/**
* Verify our algebra tag accepted
* @return void
*/
public function test_algebra() {
$text = '<algebra>a+b=c</algebra>';
$result = purify_html($text, array());
$this->assertSame($text, $result);
}
/**
* Verify our hacky multilang works
* @return void
*/
public function test_multilang() {
$text = '<lang lang="en">hmmm</lang><lang lang="anything">hm</lang>';
$result = purify_html($text, array());
$this->assertSame($text, $result);
$text = '<span lang="en" class="multilang">hmmm</span><span lang="anything" class="multilang">hm</span>';
$result = purify_html($text, array());
$this->assertSame($text, $result);
$text = '<span lang="en">hmmm</span>';
$result = purify_html($text, array());
$this->assertNotSame($text, $result);
// keep standard lang tags
$text = '<span lang="de_DU" class="multilang">asas</span>';
$result = purify_html($text, array());
$this->assertSame($text, $result);
$text = '<lang lang="de_DU">xxxxxx</lang>';
$result = purify_html($text, array());
$this->assertSame($text, $result);
}
/**
* Tests the 'allowid' option for format_text.
* @return void
*/
public function test_format_text_allowid() {
// Start off by not allowing ids (default)
$options = array(
'nocache' => true
);
$result = format_text('<div id="example">Frog</div>', FORMAT_HTML, $options);
$this->assertSame('<div>Frog</div>', $result);
// Now allow ids
$options['allowid'] = true;
$result = format_text('<div id="example">Frog</div>', FORMAT_HTML, $options);
$this->assertSame('<div id="example">Frog</div>', $result);
}
/**
* Test if linebreaks kept unchanged.
* @return void
*/
function test_line_breaking() {
$text = "\n\raa\rsss\nsss\r";
$this->assertSame($text, purify_html($text));
}
/**
* Test fixing of strict problems.
* @return void
*/
function test_tidy() {
$text = "<p>xx";
$this->assertSame('<p>xx</p>', purify_html($text));
$text = "<P>xx</P>";
$this->assertSame('<p>xx</p>', purify_html($text));
$text = "xx<br>";
$this->assertSame('xx<br />', purify_html($text));
}
/**
* Test nesting - this used to cause problems in earlier versions
* @return void
*/
function test_nested_lists() {
$text = "<ul><li>One<ul><li>Two</li></ul></li><li>Three</li></ul>";
$this->assertSame($text, purify_html($text));
}
/**
* Test that XSS protection works, complete smoke tests are in htmlpurifier itself.
* @return void
*/
function test_cleaning_nastiness() {
$text = "x<SCRIPT>alert('XSS')</SCRIPT>x";
$this->assertSame('xx', purify_html($text));
$text = '<DIV STYLE="background-image:url(javascript:alert(\'XSS\'))">xx</DIV>';
$this->assertSame('<div>xx</div>', purify_html($text));
$text = '<DIV STYLE="width:expression(alert(\'XSS\'));">xx</DIV>';
$this->assertSame('<div>xx</div>', purify_html($text));
$text = 'x<IFRAME SRC="javascript:alert(\'XSS\');"></IFRAME>x';
$this->assertSame('xx', purify_html($text));
$text = 'x<OBJECT TYPE="text/x-scriptlet" DATA="http://ha.ckers.org/scriptlet.html"></OBJECT>x';
$this->assertSame('xx', purify_html($text));
$text = 'x<EMBED SRC="http://ha.ckers.org/xss.swf" AllowScriptAccess="always"></EMBED>x';
$this->assertSame('xx', purify_html($text));
$text = 'x<form></form>x';
$this->assertSame('xx', purify_html($text));
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* Test integration of PHPUnit and custom Moodle hacks.
*
* @package core_core
* @category phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_phpunit_basic_testcase extends basic_testcase {
public function test_bootstrap() {
global $CFG;
$this->assertTrue(isset($CFG->httpswwwroot));
$this->assertEquals($CFG->httpswwwroot, $CFG->wwwroot);
$this->assertEquals($CFG->prefix, $CFG->phpunit_prefix);
}
/*
public function test_borked_tests() {
global $DB;
$DB->set_field('user', 'confirmed', 1, array('id'=>-1));
$CFG->xx = 'yy';
}
*/
}
+420
View File
@@ -0,0 +1,420 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* textlib unit teststest
*
* @package core_core
* @category phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Unit tests for our utf-8 aware text processing
*
* @package core
* @subpackage lib
* @copyright 2010 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_textlib_testcase extends basic_testcase {
public function test_parse_charset() {
$this->assertSame(textlib::parse_charset('Cp1250'), 'windows-1250');
// does typo3 work? some encoding moodle does not use
$this->assertSame(textlib::parse_charset('ms-ansi'), 'windows-1252');
}
public function test_convert() {
$utf8 = "Žluťoučký koníček";
$iso2 = pack("H*", "ae6c75bb6f75e86bfd206b6f6eede8656b");
$win = pack("H*", "8e6c759d6f75e86bfd206b6f6eede8656b");
$this->assertSame(textlib::convert($utf8, 'utf-8', 'iso-8859-2'), $iso2);
$this->assertSame(textlib::convert($iso2, 'iso-8859-2', 'utf-8'), $utf8);
$this->assertSame(textlib::convert($utf8, 'utf-8', 'win-1250'), $win);
$this->assertSame(textlib::convert($win, 'win-1250', 'utf-8'), $utf8);
$this->assertSame(textlib::convert($win, 'win-1250', 'iso-8859-2'), $iso2);
$this->assertSame(textlib::convert($iso2, 'iso-8859-2', 'win-1250'), $win);
$this->assertSame(textlib::convert($iso2, 'iso-8859-2', 'iso-8859-2'), $iso2);
$this->assertSame(textlib::convert($win, 'win-1250', 'cp1250'), $win);
$utf8 = '言語設定';
$str = pack("H*", "b8c0b8ecc0dfc4ea"); //EUC-JP
$this->assertSame(textlib::convert($utf8, 'utf-8', 'EUC-JP'), $str);
$this->assertSame(textlib::convert($str, 'EUC-JP', 'utf-8'), $utf8);
$str = pack("H*", "1b24423840386c405f446a1b2842"); //ISO-2022-JP
$this->assertSame(textlib::convert($utf8, 'utf-8', 'ISO-2022-JP'), $str);
$this->assertSame(textlib::convert($str, 'ISO-2022-JP', 'utf-8'), $utf8);
$str = pack("H*", "8cbe8cea90dd92e8"); //SHIFT-JIS
$this->assertSame(textlib::convert($utf8, 'utf-8', 'SHIFT-JIS'), $str);
$this->assertSame(textlib::convert($str, 'SHIFT-JIS', 'utf-8'), $utf8);
$utf8 = '简体中文';
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB2312
$this->assertSame(textlib::convert($utf8, 'utf-8', 'GB2312'), $str);
$this->assertSame(textlib::convert($str, 'GB2312', 'utf-8'), $utf8);
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB18030
$this->assertSame(textlib::convert($utf8, 'utf-8', 'GB18030'), $str);
$this->assertSame(textlib::convert($str, 'GB18030', 'utf-8'), $utf8);
}
public function test_substr() {
$str = "Žluťoučký koníček";
$this->assertSame(textlib::substr($str, 0), $str);
$this->assertSame(textlib::substr($str, 1), 'luťoučký koníček');
$this->assertSame(textlib::substr($str, 1, 3), 'luť');
$this->assertSame(textlib::substr($str, 0, 100), $str);
$this->assertSame(textlib::substr($str, -3, 2), 'če');
$iso2 = pack("H*", "ae6c75bb6f75e86bfd206b6f6eede8656b");
$this->assertSame(textlib::substr($iso2, 1, 3, 'iso-8859-2'), textlib::convert('luť', 'utf-8', 'iso-8859-2'));
$this->assertSame(textlib::substr($iso2, 0, 100, 'iso-8859-2'), textlib::convert($str, 'utf-8', 'iso-8859-2'));
$this->assertSame(textlib::substr($iso2, -3, 2, 'iso-8859-2'), textlib::convert('če', 'utf-8', 'iso-8859-2'));
$win = pack("H*", "8e6c759d6f75e86bfd206b6f6eede8656b");
$this->assertSame(textlib::substr($win, 1, 3, 'cp1250'), textlib::convert('luť', 'utf-8', 'cp1250'));
$this->assertSame(textlib::substr($win, 0, 100, 'cp1250'), textlib::convert($str, 'utf-8', 'cp1250'));
$this->assertSame(textlib::substr($win, -3, 2, 'cp1250'), textlib::convert('če', 'utf-8', 'cp1250'));
$str = pack("H*", "b8c0b8ecc0dfc4ea"); //EUC-JP
$s = pack("H*", "b8ec"); //EUC-JP
$this->assertSame(textlib::substr($str, 1, 1, 'EUC-JP'), $s);
$str = pack("H*", "1b24423840386c405f446a1b2842"); //ISO-2022-JP
$s = pack("H*", "1b2442386c1b2842"); //ISO-2022-JP
$this->assertSame(textlib::substr($str, 1, 1, 'ISO-2022-JP'), $s);
$str = pack("H*", "8cbe8cea90dd92e8"); //SHIFT-JIS
$s = pack("H*", "8cea"); //SHIFT-JIS
$this->assertSame(textlib::substr($str, 1, 1, 'SHIFT-JIS'), $s);
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB2312
$s = pack("H*", "cce5"); //GB2312
$this->assertSame(textlib::substr($str, 1, 1, 'GB2312'), $s);
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB18030
$s = pack("H*", "cce5"); //GB18030
$this->assertSame(textlib::substr($str, 1, 1, 'GB18030'), $s);
}
public function test_strlen() {
$str = "Žluťoučký koníček";
$this->assertSame(textlib::strlen($str), 17);
$iso2 = pack("H*", "ae6c75bb6f75e86bfd206b6f6eede8656b");
$this->assertSame(textlib::strlen($iso2, 'iso-8859-2'), 17);
$win = pack("H*", "8e6c759d6f75e86bfd206b6f6eede8656b");
$this->assertSame(textlib::strlen($win, 'cp1250'), 17);
$str = pack("H*", "b8ec"); //EUC-JP
$this->assertSame(textlib::strlen($str, 'EUC-JP'), 1);
$str = pack("H*", "b8c0b8ecc0dfc4ea"); //EUC-JP
$this->assertSame(textlib::strlen($str, 'EUC-JP'), 4);
$str = pack("H*", "1b2442386c1b2842"); //ISO-2022-JP
$this->assertSame(textlib::strlen($str, 'ISO-2022-JP'), 1);
$str = pack("H*", "1b24423840386c405f446a1b2842"); //ISO-2022-JP
$this->assertSame(textlib::strlen($str, 'ISO-2022-JP'), 4);
$str = pack("H*", "8cea"); //SHIFT-JIS
$this->assertSame(textlib::strlen($str, 'SHIFT-JIS'), 1);
$str = pack("H*", "8cbe8cea90dd92e8"); //SHIFT-JIS
$this->assertSame(textlib::strlen($str, 'SHIFT-JIS'), 4);
$str = pack("H*", "cce5"); //GB2312
$this->assertSame(textlib::strlen($str, 'GB2312'), 1);
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB2312
$this->assertSame(textlib::strlen($str, 'GB2312'), 4);
$str = pack("H*", "cce5"); //GB18030
$this->assertSame(textlib::strlen($str, 'GB18030'), 1);
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB18030
$this->assertSame(textlib::strlen($str, 'GB18030'), 4);
}
public function test_strtolower() {
$str = "Žluťoučký koníček";
$low = 'žluťoučký koníček';
$this->assertSame(textlib::strtolower($str), $low);
$iso2 = pack("H*", "ae6c75bb6f75e86bfd206b6f6eede8656b");
$this->assertSame(textlib::strtolower($iso2, 'iso-8859-2'), textlib::convert($low, 'utf-8', 'iso-8859-2'));
$win = pack("H*", "8e6c759d6f75e86bfd206b6f6eede8656b");
$this->assertSame(textlib::strtolower($win, 'cp1250'), textlib::convert($low, 'utf-8', 'cp1250'));
$str = '言語設定';
$this->assertSame(textlib::strtolower($str), $str);
$str = '简体中文';
$this->assertSame(textlib::strtolower($str), $str);
$str = pack("H*", "1b24423840386c405f446a1b2842"); //ISO-2022-JP
$this->assertSame(textlib::strtolower($str, 'ISO-2022-JP'), $str);
$str = pack("H*", "8cbe8cea90dd92e8"); //SHIFT-JIS
$this->assertSame(textlib::strtolower($str, 'SHIFT-JIS'), $str);
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB2312
$this->assertSame(textlib::strtolower($str, 'GB2312'), $str);
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB18030
$this->assertSame(textlib::strtolower($str, 'GB18030'), $str);
}
public function test_strtoupper() {
$str = "Žluťoučký koníček";
$up = 'ŽLUŤOUČKÝ KONÍČEK';
$this->assertSame(textlib::strtoupper($str), $up);
$iso2 = pack("H*", "ae6c75bb6f75e86bfd206b6f6eede8656b");
$this->assertSame(textlib::strtoupper($iso2, 'iso-8859-2'), textlib::convert($up, 'utf-8', 'iso-8859-2'));
$win = pack("H*", "8e6c759d6f75e86bfd206b6f6eede8656b");
$this->assertSame(textlib::strtoupper($win, 'cp1250'), textlib::convert($up, 'utf-8', 'cp1250'));
$str = '言語設定';
$this->assertSame(textlib::strtoupper($str), $str);
$str = '简体中文';
$this->assertSame(textlib::strtoupper($str), $str);
$str = pack("H*", "1b24423840386c405f446a1b2842"); //ISO-2022-JP
$this->assertSame(textlib::strtoupper($str, 'ISO-2022-JP'), $str);
$str = pack("H*", "8cbe8cea90dd92e8"); //SHIFT-JIS
$this->assertSame(textlib::strtoupper($str, 'SHIFT-JIS'), $str);
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB2312
$this->assertSame(textlib::strtoupper($str, 'GB2312'), $str);
$str = pack("H*", "bcf2cce5d6d0cec4"); //GB18030
$this->assertSame(textlib::strtoupper($str, 'GB18030'), $str);
}
public function test_strpos() {
$str = "Žluťoučký koníček";
$this->assertSame(textlib::strpos($str, 'koníč'), 10);
}
public function test_strrpos() {
$str = "Žluťoučký koníček";
$this->assertSame(textlib::strrpos($str, 'o'), 11);
}
public function test_specialtoascii() {
$str = "Žluťoučký koníček";
$this->assertSame(textlib::specialtoascii($str), 'Zlutoucky konicek');
}
public function test_encode_mimeheader() {
$str = "Žluťoučký koníček";
$this->assertSame(textlib::encode_mimeheader($str), '=?utf-8?B?xb1sdcWlb3XEjWvDvSBrb27DrcSNZWs=?=');
}
public function test_entities_to_utf8() {
$str = "&#x17d;lu&#x165;ou&#x10d;k&#xfd; kon&#237;&#269;ek";
$this->assertSame(textlib::entities_to_utf8($str), "Žluťoučký koníček");
}
public function test_utf8_to_entities() {
$str = "Žluťoučký koníček";
$this->assertSame(textlib::utf8_to_entities($str), "&#x17d;lu&#x165;ou&#x10d;k&#xfd; kon&#xed;&#x10d;ek");
$this->assertSame(textlib::utf8_to_entities($str, true), "&#381;lu&#357;ou&#269;k&#253; kon&#237;&#269;ek");
}
public function test_trim_utf8_bom() {
$bom = "\xef\xbb\xbf";
$str = "Žluťoučký koníček";
$this->assertSame(textlib::trim_utf8_bom($bom.$str.$bom), $str.$bom);
}
public function test_get_encodings() {
$encodings = textlib::get_encodings();
$this->assertTrue(is_array($encodings));
$this->assertTrue(count($encodings) > 1);
$this->assertTrue(isset($encodings['UTF-8']));
}
public function test_code2utf8() {
$this->assertSame(textlib::code2utf8(381), 'Ž');
}
public function test_strtotitle() {
$str = "žluťoučký koníček";
$this->assertSame(textlib::strtotitle($str), "Žluťoučký Koníček");
}
public function test_deprecated_textlib_get_instance() {
ob_start();
$textlib = textlib_get_instance();
$output = ob_get_contents();
$this->assertNotEmpty($output);
ob_end_clean();
$this->assertSame($textlib->substr('abc', 1, 1), 'b');
$this->assertSame($textlib->strlen('abc'), 3);
$this->assertSame($textlib->strtoupper('Abc'), 'ABC');
$this->assertSame($textlib->strtolower('Abc'), 'abc');
$this->assertSame($textlib->strpos('abc', 'a'), 0);
$this->assertSame($textlib->strpos('abc', 'd'), false);
$this->assertSame($textlib->strrpos('abcabc', 'a'), 3);
$this->assertSame($textlib->specialtoascii('ábc'), 'abc');
$this->assertSame($textlib->strtotitle('abc ABC'), 'Abc Abc');
}
}
/**
* Unit tests for our utf-8 aware collator.
*
* Used for sorting.
*
* @package core
* @subpackage lib
* @copyright 2011 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class collatorlib_testcase extends basic_testcase {
protected $initiallang = null;
protected $error = null;
public function setUp() {
global $SESSION;
if (isset($SESSION->lang)) {
$this->initiallang = $SESSION->lang;
}
$SESSION->lang = 'en'; // make sure we test en language to get consistent results, hopefully all systems have this locale
if (extension_loaded('intl')) {
$this->error = 'Collation aware sorting not supported';
} else {
$this->error = 'Collation aware sorting not supported, PHP extension "intl" is not available.';
}
parent::setUp();
}
public function tearDown() {
global $SESSION;
parent::tearDown();
if ($this->initiallang !== null) {
$SESSION->lang = $this->initiallang;
$this->initiallang = null;
} else {
unset($SESSION->lang);
}
}
function test_asort() {
$arr = array('b' => 'ab', 1 => 'aa', 0 => 'cc');
collatorlib::asort($arr);
$this->assertSame(array_keys($arr), array(1, 'b', 0));
$this->assertSame(array_values($arr), array('aa', 'ab', 'cc'));
$arr = array('a' => 'áb', 'b' => 'ab', 1 => 'aa', 0=>'cc');
collatorlib::asort($arr);
$this->assertSame(array_keys($arr), array(1, 'b', 'a', 0), $this->error);
$this->assertSame(array_values($arr), array('aa', 'ab', 'áb', 'cc'), $this->error);
}
function test_asort_objects_by_method() {
$objects = array(
'b' => new string_test_class('ab'),
1 => new string_test_class('aa'),
0 => new string_test_class('cc')
);
collatorlib::asort_objects_by_method($objects, 'get_protected_name');
$this->assertSame(array_keys($objects), array(1, 'b', 0));
$this->assertSame($this->get_ordered_names($objects, 'get_protected_name'), array('aa', 'ab', 'cc'));
$objects = array(
'a' => new string_test_class('áb'),
'b' => new string_test_class('ab'),
1 => new string_test_class('aa'),
0 => new string_test_class('cc')
);
collatorlib::asort_objects_by_method($objects, 'get_private_name');
$this->assertSame(array_keys($objects), array(1, 'b', 'a', 0), $this->error);
$this->assertSame($this->get_ordered_names($objects, 'get_private_name'), array('aa', 'ab', 'áb', 'cc'), $this->error);
}
function test_asort_objects_by_property() {
$objects = array(
'b' => new string_test_class('ab'),
1 => new string_test_class('aa'),
0 => new string_test_class('cc')
);
collatorlib::asort_objects_by_property($objects, 'publicname');
$this->assertSame(array_keys($objects), array(1, 'b', 0));
$this->assertSame($this->get_ordered_names($objects, 'publicname'), array('aa', 'ab', 'cc'));
$objects = array(
'a' => new string_test_class('áb'),
'b' => new string_test_class('ab'),
1 => new string_test_class('aa'),
0 => new string_test_class('cc')
);
collatorlib::asort_objects_by_property($objects, 'publicname');
$this->assertSame(array_keys($objects), array(1, 'b', 'a', 0), $this->error);
$this->assertSame($this->get_ordered_names($objects, 'publicname'), array('aa', 'ab', 'áb', 'cc'), $this->error);
}
protected function get_ordered_names($objects, $methodproperty = 'get_protected_name') {
$return = array();
foreach ($objects as $object) {
if ($methodproperty == 'publicname') {
$return[] = $object->publicname;
} else {
$return[] = $object->$methodproperty();
}
}
return $return;
}
}
/**
* Simple class used to work with the unit test.
*
* @package core
* @subpackage lib
* @copyright 2011 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class string_test_class extends stdClass {
public $publicname;
protected $protectedname;
private $privatename;
public function __construct($name) {
$this->publicname = $name;
$this->protectedname = $name;
$this->privatename = $name;
}
public function get_protected_name() {
return $this->protectedname;
}
public function get_private_name() {
return $this->publicname;
}
}
+4 -1
View File
@@ -2742,7 +2742,10 @@ function debugging($message = '', $level = DEBUG_NORMAL, $backtrace = null) {
$backtrace = debug_backtrace();
}
$from = format_backtrace($backtrace, CLI_SCRIPT);
if (!empty($UNITTEST->running)) {
if (PHPUNITTEST) {
echo 'Debugging: ' . $message . "\n" . $from;
} else if (!empty($UNITTEST->running)) {
// When the unit tests are running, any call to trigger_error
// is intercepted by the test framework and reported as an exception.
// Therefore, we cannot use trigger_error during unit tests.
+2
View File
@@ -50,6 +50,8 @@
defined('MOODLE_INTERNAL') || die;
global $CFG;
require_once($CFG->dirroot . '/mod/lti/locallib.php');
require_once($CFG->dirroot . '/mod/lti/servicelib.php');
@@ -20,6 +20,8 @@ if (!defined('MOODLE_INTERNAL')) {
// Unit tests for scorm_formatduration function from locallib.php
global $CFG;
// Make sure the code being tested is accessible.
require_once($CFG->dirroot . '/mod/scorm/locallib.php'); // Include the code to test
+2 -2
View File
@@ -40,9 +40,9 @@ require_once("$CFG->dirroot/mod/url/lib.php");
function url_appears_valid_url($url) {
if (preg_match('/^(\/|https?:|ftp:)/i', $url)) {
// note: this is not exact validation, we look for severely malformed URLs only
return preg_match('/^[a-z]+:\/\/([^:@\s]+:[^@\s]+@)?[a-z0-9_\.\-]+(:[0-9]+)?(\/[^#]*)?(#.*)?$/i', $url);
return (bool)preg_match('/^[a-z]+:\/\/([^:@\s]+:[^@\s]+@)?[a-z0-9_\.\-]+(:[0-9]+)?(\/[^#]*)?(#.*)?$/i', $url);
} else {
return preg_match('/^[a-z]+:\/\/...*$/i', $url);
return (bool)preg_match('/^[a-z]+:\/\/...*$/i', $url);
}
}
+2
View File
@@ -26,6 +26,8 @@
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/url/locallib.php');
+67
View File
@@ -0,0 +1,67 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for some mod URL lib stuff.
*
* @package mod_url
* @category phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* mod_url tests
*
* @package mod_url
* @category phpunit
* @copyright 2011 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_url_lib_testcase extends basic_testcase {
public static function setUpBeforeClass() {
global $CFG;
require_once($CFG->dirroot . '/mod/url/locallib.php');
}
public function test_url_appears_valid_url() {
$this->assertTrue(url_appears_valid_url('http://example'));
$this->assertTrue(url_appears_valid_url('http://www.example.com'));
$this->assertTrue(url_appears_valid_url('http://www.exa-mple2.com'));
$this->assertTrue(url_appears_valid_url('http://www.example.com/~nobody/index.html'));
$this->assertTrue(url_appears_valid_url('http://www.example.com#hmm'));
$this->assertTrue(url_appears_valid_url('http://www.example.com/#hmm'));
$this->assertTrue(url_appears_valid_url('http://www.example.com/žlutý koníček/lala.txt'));
$this->assertTrue(url_appears_valid_url('http://www.example.com/žlutý koníček/lala.txt#hmmmm'));
$this->assertTrue(url_appears_valid_url('http://www.example.com/index.php?xx=yy&zz=aa'));
$this->assertTrue(url_appears_valid_url('https://user:[email protected]/žlutý koníček/lala.txt'));
$this->assertTrue(url_appears_valid_url('ftp://user:[email protected]/žlutý koníček/lala.txt'));
$this->assertFalse(url_appears_valid_url('http:example.com'));
$this->assertFalse(url_appears_valid_url('http:/example.com'));
$this->assertFalse(url_appears_valid_url('http://'));
$this->assertFalse(url_appears_valid_url('http://www.exa mple.com'));
$this->assertFalse(url_appears_valid_url('http://www.examplé.com'));
$this->assertFalse(url_appears_valid_url('http://@www.example.com'));
$this->assertFalse(url_appears_valid_url('http://user:@www.example.com'));
$this->assertTrue(url_appears_valid_url('lalala://@:@/'));
}
}
+2
View File
@@ -35,6 +35,8 @@ if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
global $CFG;
require_once($CFG->dirroot . '/mod/wiki/parser/parser.php');
class wikiparser_test extends UnitTestCase {
+30
View File
@@ -0,0 +1,30 @@
<phpunit
bootstrap="lib/phpunit/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
backupGlobals="false"
backupStaticAttributes="false"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
strict="false"
verbose="false"
>
<!--All core suites need to be added manually here-->
<testsuites>
<testsuite name="core_lib">
<directory suffix="_test.php">lib/tests</directory>
</testsuite>
</testsuites>
<!--Plugin suites: use admin/tool/phpunit/cli/util.php to build phpunit.xml from phpunit.xml.dist with up-to-date list of plugins in current install-->
<!--@plugin_suits_start@-->
<!--@plugin_suits_end@-->
</phpunit>