diff --git a/.gitignore b/.gitignore
index 5a123491042..a57c23f7201 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@ CVS
/.project
/.buildpath
/.cache
+/phpunit.xml
\ No newline at end of file
diff --git a/admin/tool/phpunit/cli/util.php b/admin/tool/phpunit/cli/util.php
new file mode 100644
index 00000000000..c270e172633
--- /dev/null
+++ b/admin/tool/phpunit/cli/util.php
@@ -0,0 +1,93 @@
+.
+
+/**
+ * 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);
+}
diff --git a/admin/tool/phpunit/index.php b/admin/tool/phpunit/index.php
new file mode 100644
index 00000000000..5c55e7091db
--- /dev/null
+++ b/admin/tool/phpunit/index.php
@@ -0,0 +1,39 @@
+.
+
+/**
+ * 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();
\ No newline at end of file
diff --git a/admin/tool/phpunit/lang/en/tool_phpunit.php b/admin/tool/phpunit/lang/en/tool_phpunit.php
new file mode 100644
index 00000000000..08dbbbdb395
--- /dev/null
+++ b/admin/tool/phpunit/lang/en/tool_phpunit.php
@@ -0,0 +1,25 @@
+.
+
+/**
+ * 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';
diff --git a/admin/tool/phpunit/settings.php b/admin/tool/phpunit/settings.php
new file mode 100644
index 00000000000..8e06b2f4b78
--- /dev/null
+++ b/admin/tool/phpunit/settings.php
@@ -0,0 +1,28 @@
+.
+
+/**
+ * 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"));
diff --git a/admin/tool/phpunit/version.php b/admin/tool/phpunit/version.php
new file mode 100644
index 00000000000..e745026f858
--- /dev/null
+++ b/admin/tool/phpunit/version.php
@@ -0,0 +1,30 @@
+.
+
+/**
+ * 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)
+
diff --git a/config-dist.php b/config-dist.php
index abc8da1b26d..e4fe58fba35 100644
--- a/config-dist.php
+++ b/config-dist.php
@@ -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
diff --git a/lib/accesslib.php b/lib/accesslib.php
index bd45588f988..62e7337ad65 100644
--- a/lib/accesslib.php
+++ b/lib/accesslib.php
@@ -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.');
}
diff --git a/lib/ddl/database_manager.php b/lib/ddl/database_manager.php
index 22d24e43b13..c4409d02d01 100644
--- a/lib/ddl/database_manager.php
+++ b/lib/ddl/database_manager.php
@@ -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');
diff --git a/lib/ddl/simpletest/testddl.php b/lib/ddl/simpletest/testddl.php
index 4a27f80cf94..5d452a0396e 100644
--- a/lib/ddl/simpletest/testddl.php
+++ b/lib/ddl/simpletest/testddl.php
@@ -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 {
diff --git a/lib/moodlelib.php b/lib/moodlelib.php
index 0099f60bfc9..fd2765d254b 100644
--- a/lib/moodlelib.php
+++ b/lib/moodlelib.php
@@ -7763,7 +7763,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);
diff --git a/lib/phpunit/bootstrap.php b/lib/phpunit/bootstrap.php
new file mode 100644
index 00000000000..0dd6106423d
--- /dev/null
+++ b/lib/phpunit/bootstrap.php
@@ -0,0 +1,208 @@
+.
+
+/**
+ * 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);
+ }
+}
diff --git a/lib/phpunit/lib.php b/lib/phpunit/lib.php
new file mode 100644
index 00000000000..41f7974e94d
--- /dev/null
+++ b/lib/phpunit/lib.php
@@ -0,0 +1,602 @@
+.
+
+/**
+ * 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 = '
+
+
+ @dir@
+
+ ';
+ $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('|.*|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
+ }
+}
+
diff --git a/lib/phpunit/readme.md b/lib/phpunit/readme.md
new file mode 100644
index 00000000000..5a67f11f05a
--- /dev/null
+++ b/lib/phpunit/readme.md
@@ -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
diff --git a/lib/setup.php b/lib/setup.php
index 2f0f1e4f1fa..d5febeb2c61 100644
--- a/lib/setup.php
+++ b/lib/setup.php
@@ -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
diff --git a/lib/simpletest/testcomponentlib.php b/lib/simpletest/testcomponentlib.php
index 13d15438efa..44a0fe2ced0 100644
--- a/lib/simpletest/testcomponentlib.php
+++ b/lib/simpletest/testcomponentlib.php
@@ -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 {
diff --git a/lib/simpletest/testcsslib.php b/lib/simpletest/testcsslib.php
index b69a8a5a10b..abe3e1b3552 100644
--- a/lib/simpletest/testcsslib.php
+++ b/lib/simpletest/testcsslib.php
@@ -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');
diff --git a/lib/simpletest/testexternallib.php b/lib/simpletest/testexternallib.php
index 709d226f52b..7e1f14a3879 100644
--- a/lib/simpletest/testexternallib.php
+++ b/lib/simpletest/testexternallib.php
@@ -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 {
diff --git a/lib/simpletest/testfilelib.php b/lib/simpletest/testfilelib.php
index ffea131f2bc..0e63fc8a439 100644
--- a/lib/simpletest/testfilelib.php
+++ b/lib/simpletest/testfilelib.php
@@ -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 {
diff --git a/lib/simpletest/testformslib.php b/lib/simpletest/testformslib.php
index 2d8d788e2cf..7b41c302eb0 100644
--- a/lib/simpletest/testformslib.php
+++ b/lib/simpletest/testformslib.php
@@ -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');
diff --git a/lib/simpletest/testhtmlwriter.php b/lib/simpletest/testhtmlwriter.php
index ed3a1e0b7d7..b22c92c2307 100644
--- a/lib/simpletest/testhtmlwriter.php
+++ b/lib/simpletest/testhtmlwriter.php
@@ -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');
diff --git a/lib/simpletest/testnavigationlib.php b/lib/simpletest/testnavigationlib.php
index fdb5f4f4d09..be5493465ce 100644
--- a/lib/simpletest/testnavigationlib.php
+++ b/lib/simpletest/testnavigationlib.php
@@ -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');
}
diff --git a/lib/simpletest/testoutputcomponents.php b/lib/simpletest/testoutputcomponents.php
index f8aa788b2ef..6ad6ccc4e20 100644
--- a/lib/simpletest/testoutputcomponents.php
+++ b/lib/simpletest/testoutputcomponents.php
@@ -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);
diff --git a/lib/simpletest/testquestionlib.php b/lib/simpletest/testquestionlib.php
index 2e1f205beb9..6ebacdbd8ea 100644
--- a/lib/simpletest/testquestionlib.php
+++ b/lib/simpletest/testquestionlib.php
@@ -26,6 +26,8 @@
defined('MOODLE_INTERNAL') || die();
+global $CFG;
+
require_once($CFG->libdir . '/questionlib.php');
diff --git a/lib/simpletest/testrss.php b/lib/simpletest/testrss.php
index add6b099535..d154c8db392 100644
--- a/lib/simpletest/testrss.php
+++ b/lib/simpletest/testrss.php
@@ -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 {
diff --git a/lib/tests/htmlpurifier_test.php b/lib/tests/htmlpurifier_test.php
new file mode 100644
index 00000000000..a43b7d05580
--- /dev/null
+++ b/lib/tests/htmlpurifier_test.php
@@ -0,0 +1,185 @@
+.
+
+/**
+ * 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 = 'Some link';
+ $result = format_text($text, FORMAT_HTML);
+ $this->assertSame($text, $result);
+
+ $result = format_text('Some link', FORMAT_HTML);
+ $this->assertSame('Some link', $result);
+ }
+
+ /**
+ * Verify our nolink tag accepted
+ * @return void
+ */
+ public function test_nolink() {
+ // we can not use format text because nolink changes result
+ $text = 'no filters
';
+ $result = purify_html($text, array());
+ $this->assertSame($text, $result);
+
+ $text = 'xxxxxxxx
';
+ $result = purify_html($text, array());
+ $this->assertSame($text, $result);
+ }
+
+ /**
+ * Verify our tex tag accepted
+ * @return void
+ */
+ public function test_tex() {
+ $text = 'a+b=c';
+ $result = purify_html($text, array());
+ $this->assertSame($text, $result);
+ }
+
+ /**
+ * Verify our algebra tag accepted
+ * @return void
+ */
+ public function test_algebra() {
+ $text = 'a+b=c';
+ $result = purify_html($text, array());
+ $this->assertSame($text, $result);
+ }
+
+ /**
+ * Verify our hacky multilang works
+ * @return void
+ */
+ public function test_multilang() {
+ $text = 'hmmmhm';
+ $result = purify_html($text, array());
+ $this->assertSame($text, $result);
+
+ $text = 'hmmmhm';
+ $result = purify_html($text, array());
+ $this->assertSame($text, $result);
+
+ $text = 'hmmm';
+ $result = purify_html($text, array());
+ $this->assertNotSame($text, $result);
+
+ // keep standard lang tags
+
+ $text = 'asas';
+ $result = purify_html($text, array());
+ $this->assertSame($text, $result);
+
+ $text = 'xxxxxx';
+ $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('
Frog
', FORMAT_HTML, $options);
+ $this->assertSame('Frog
', $result);
+
+ // Now allow ids
+ $options['allowid'] = true;
+ $result = format_text('Frog
', FORMAT_HTML, $options);
+ $this->assertSame('Frog
', $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 = "xx";
+ $this->assertSame('
xx
', purify_html($text));
+
+ $text = "xx
";
+ $this->assertSame('xx
', purify_html($text));
+
+ $text = "xx
";
+ $this->assertSame('xx
', purify_html($text));
+ }
+
+ /**
+ * Test nesting - this used to cause problems in earlier versions
+ * @return void
+ */
+ function test_nested_lists() {
+ $text = "";
+ $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 = "xx";
+ $this->assertSame('xx', purify_html($text));
+
+ $text = 'xx
';
+ $this->assertSame('xx
', purify_html($text));
+
+ $text = 'xx
';
+ $this->assertSame('xx
', purify_html($text));
+
+ $text = 'xx';
+ $this->assertSame('xx', purify_html($text));
+
+ $text = 'xx';
+ $this->assertSame('xx', purify_html($text));
+
+ $text = 'xx';
+ $this->assertSame('xx', purify_html($text));
+
+ $text = 'xx';
+ $this->assertSame('xx', purify_html($text));
+ }
+}
+
diff --git a/lib/tests/phpunit_test.php b/lib/tests/phpunit_test.php
new file mode 100644
index 00000000000..90ec3daabe2
--- /dev/null
+++ b/lib/tests/phpunit_test.php
@@ -0,0 +1,45 @@
+.
+
+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';
+ }
+*/
+
+}
\ No newline at end of file
diff --git a/lib/tests/textlib_test.php b/lib/tests/textlib_test.php
new file mode 100644
index 00000000000..9a7369149a9
--- /dev/null
+++ b/lib/tests/textlib_test.php
@@ -0,0 +1,420 @@
+.
+
+/**
+ * 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 = "Žluťoučký koníč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), "Žluťoučký koníček");
+ $this->assertSame(textlib::utf8_to_entities($str, true), "Žluťoučký koníč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;
+ }
+}
diff --git a/lib/weblib.php b/lib/weblib.php
index 7de357d4f5b..22141d27b4e 100644
--- a/lib/weblib.php
+++ b/lib/weblib.php
@@ -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.
diff --git a/mod/lti/simpletest/testlocallib.php b/mod/lti/simpletest/testlocallib.php
index aaeeb42f6d3..62acc3f2b9d 100644
--- a/mod/lti/simpletest/testlocallib.php
+++ b/mod/lti/simpletest/testlocallib.php
@@ -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');
diff --git a/mod/scorm/simpletest/test_formatduration.php b/mod/scorm/simpletest/test_formatduration.php
index 2d2a808e1dc..cd8430ceaa7 100644
--- a/mod/scorm/simpletest/test_formatduration.php
+++ b/mod/scorm/simpletest/test_formatduration.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
diff --git a/mod/url/locallib.php b/mod/url/locallib.php
index 70e7efc1b9e..070f0e59413 100644
--- a/mod/url/locallib.php
+++ b/mod/url/locallib.php
@@ -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);
}
}
diff --git a/mod/url/simpletest/testlib.php b/mod/url/simpletest/testlib.php
index 2dbf1bab845..18a026edab2 100644
--- a/mod/url/simpletest/testlib.php
+++ b/mod/url/simpletest/testlib.php
@@ -26,6 +26,8 @@
defined('MOODLE_INTERNAL') || die();
+global $CFG;
+
require_once($CFG->dirroot . '/mod/url/locallib.php');
diff --git a/mod/url/tests/lib_test.php b/mod/url/tests/lib_test.php
new file mode 100644
index 00000000000..752bc677ad8
--- /dev/null
+++ b/mod/url/tests/lib_test.php
@@ -0,0 +1,67 @@
+.
+
+/**
+ * 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:password@www.example.com/žlutý koníček/lala.txt'));
+ $this->assertTrue(url_appears_valid_url('ftp://user:password@www.example.com/ž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://@:@/'));
+ }
+}
\ No newline at end of file
diff --git a/mod/wiki/simpletest/testwikiparser.php b/mod/wiki/simpletest/testwikiparser.php
index 9d197abbd95..024642e0ded 100644
--- a/mod/wiki/simpletest/testwikiparser.php
+++ b/mod/wiki/simpletest/testwikiparser.php
@@ -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 {
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 00000000000..09456e10c22
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+ lib/tests
+
+
+
+
+
+
+
+